BUG-4556: Introduce StmtContext.getSchemaPath()
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / effective / ExtendedTypeEffectiveStatementImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Splitter;
12 import com.google.common.collect.ImmutableList;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
18 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
19 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
25 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
28 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
29 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
30 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
33 import org.opendaylight.yangtools.yang.model.util.ExtendedType.Builder;
34 import org.opendaylight.yangtools.yang.parser.spi.TypeNamespace;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
36 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.TypeUtils;
37 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
38 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthEffectiveStatementImpl;
39 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.PatternEffectiveStatementImpl;
40 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeEffectiveStatementImpl;
41 import org.opendaylight.yangtools.yang.parser.util.TypeConstraints;
42
43 public final class ExtendedTypeEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<String, TypeStatement>
44 implements
45         TypeDefinition<TypeDefinition<?>>, TypeEffectiveStatement<TypeStatement> {
46
47     private static final Splitter COLON_SPLITTER = Splitter.on(':').trimResults();
48
49     private final QName qName;
50     private final SchemaPath path;
51     private final TypeDefinition<?> baseType;
52     private final String defaultValue;
53     private final String units;
54     private final List<RangeConstraint> ranges;
55     private final List<LengthConstraint> lengths;
56     private final List<PatternConstraint> patterns;
57     private final Integer fractionDigits;
58
59     private ExtendedType extendedType = null;
60     private final boolean isExtended;
61
62     public ExtendedTypeEffectiveStatementImpl(
63             final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx, final boolean isExtended) {
64         super(ctx);
65
66         this.isExtended = isExtended;
67         qName = initQName(ctx, isExtended);
68
69         final StmtContext<?, TypedefStatement, TypedefEffectiveStatement> typeStmt =
70                 ctx.getFromNamespace(TypeNamespace.class, qName);
71         if (typeStmt == null) {
72             path = ctx.getSchemaPath().get();
73         } else {
74             path = ctx.getFromNamespace(TypeNamespace.class, qName).getSchemaPath().get();
75         }
76
77         UnitsEffectiveStatementImpl unitsStmt = firstEffective(UnitsEffectiveStatementImpl.class);
78         this.units = (unitsStmt == null) ? null : unitsStmt.argument();
79         DefaultEffectiveStatementImpl defaultStmt = firstEffective(DefaultEffectiveStatementImpl.class);
80         this.defaultValue = (defaultStmt == null) ? null : defaultStmt.argument();
81
82         ranges = initRanges();
83         lengths = initLengths();
84         patterns = initPatterns();
85         fractionDigits = initFractionDigits();
86
87         baseType = parseBaseTypeFromCtx(ctx);
88         validateTypeConstraints(ctx);
89     }
90
91     private static QName initQName(final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
92             final boolean isExtended) {
93
94         QName qName;
95
96         if (isExtended) {
97             final List<String> nameTokens = COLON_SPLITTER.splitToList(ctx.getStatementArgument());
98
99             switch (nameTokens.size()) {
100             case 1:
101                 qName = QName.create(Utils.getRootModuleQName(ctx), nameTokens.get(0));
102                 break;
103             case 2:
104                 qName = QName.create(Utils.getRootModuleQName(ctx), nameTokens.get(1));
105                 break;
106             default:
107                 throw new IllegalArgumentException(String.format(
108                         "Bad colon separated parts number (%d) of QName '%s'.", nameTokens.size(),
109                         ctx.getStatementArgument()));
110             }
111         } else {
112             qName = Utils.qNameFromArgument(ctx, ctx.getStatementArgument());
113         }
114         return qName;
115     }
116
117     private static TypeDefinition<?> parseBaseTypeFromCtx(
118             final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx) {
119
120         TypeDefinition<?> baseType;
121
122         final QName baseTypeQName = Utils.qNameFromArgument(ctx, ctx.getStatementArgument());
123         if (TypeUtils.isYangPrimitiveTypeString(baseTypeQName.getLocalName())) {
124             baseType = TypeUtils.getYangPrimitiveTypeFromString(baseTypeQName.getLocalName());
125         } else {
126             StmtContext<?, TypedefStatement, TypedefEffectiveStatement> baseTypeCtx = ctx
127                     .getParentContext().getFromNamespace(TypeNamespace.class, baseTypeQName);
128
129             if (baseTypeCtx == null) {
130                 throw new IllegalStateException(String.format("Type '%s' was not found in %s.", baseTypeQName,
131                         ctx.getStatementSourceReference()));
132             }
133
134             baseType = (TypeDefEffectiveStatementImpl) baseTypeCtx.buildEffective();
135         }
136
137         return baseType;
138     }
139
140     private void validateTypeConstraints(
141             final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx) {
142
143         final List<String> sourceParts = COLON_SPLITTER.splitToList(ctx.getStatementSourceReference().toString());
144         TypeConstraints typeConstraints = new TypeConstraints(sourceParts.get(0), Integer.parseInt(sourceParts.get(1)));
145
146         typeConstraints.addRanges(ranges);
147         typeConstraints.addLengths(lengths);
148         typeConstraints.addPatterns(patterns);
149         typeConstraints.addFractionDigits(fractionDigits);
150
151         typeConstraints = addConstraintsFromBaseType(typeConstraints, baseType);
152         typeConstraints.validateConstraints();
153     }
154
155     private static TypeConstraints addConstraintsFromBaseType(final TypeConstraints typeConstraints,
156             final TypeDefinition<?> baseType) {
157
158         final String baseTypeName = baseType.getQName().getLocalName();
159
160         if (baseType instanceof IntegerTypeDefinition) {
161             final IntegerTypeDefinition intType = (IntegerTypeDefinition) TypeUtils
162                     .getYangPrimitiveTypeFromString(baseTypeName);
163             typeConstraints.addRanges(intType.getRangeConstraints());
164         } else if (baseType instanceof UnsignedIntegerTypeDefinition) {
165             final UnsignedIntegerTypeDefinition uintType = (UnsignedIntegerTypeDefinition) TypeUtils
166                     .getYangPrimitiveTypeFromString(baseTypeName);
167             typeConstraints.addRanges(uintType.getRangeConstraints());
168         } else if (baseType instanceof StringTypeDefinition) {
169             final StringTypeDefinition stringType = (StringTypeDefinition) TypeUtils
170                     .getYangPrimitiveTypeFromString(baseTypeName);
171             typeConstraints.addLengths(stringType.getLengthConstraints());
172             typeConstraints.addPatterns(stringType.getPatternConstraints());
173         } else if (baseType instanceof BinaryTypeDefinition) {
174             final BinaryTypeDefinition binaryType = (BinaryTypeDefinition) TypeUtils
175                     .getYangPrimitiveTypeFromString(baseTypeName);
176             typeConstraints.addLengths(binaryType.getLengthConstraints());
177         } else if (baseType instanceof TypeDefEffectiveStatementImpl) {
178             typeConstraints.addRanges(((TypeDefEffectiveStatementImpl) baseType).getRangeConstraints());
179             typeConstraints.addLengths(((TypeDefEffectiveStatementImpl) baseType).getLengthConstraints());
180             typeConstraints.addPatterns(((TypeDefEffectiveStatementImpl) baseType).getPatternConstraints());
181             typeConstraints.addFractionDigits(((TypeDefEffectiveStatementImpl) baseType).getFractionDigits());
182         }
183
184         return typeConstraints;
185     }
186
187     protected final Integer initFractionDigits() {
188         final FractionDigitsEffectiveStatementImpl fractionDigitsEffStmt = firstEffective(FractionDigitsEffectiveStatementImpl.class);
189         return fractionDigitsEffStmt != null ? fractionDigitsEffStmt.argument() : null;
190     }
191
192     protected final List<RangeConstraint> initRanges() {
193         final RangeEffectiveStatementImpl rangeConstraints = firstEffective(RangeEffectiveStatementImpl.class);
194         return rangeConstraints != null ? rangeConstraints.argument() : Collections.<RangeConstraint> emptyList();
195     }
196
197     protected final List<LengthConstraint> initLengths() {
198         final LengthEffectiveStatementImpl lengthConstraints = firstEffective(LengthEffectiveStatementImpl.class);
199         return lengthConstraints != null ? lengthConstraints.argument() : Collections.<LengthConstraint> emptyList();
200     }
201
202     protected final List<PatternConstraint> initPatterns() {
203         final List<PatternConstraint> patternConstraints = new ArrayList<>();
204
205         for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
206             if (effectiveStatement instanceof PatternEffectiveStatementImpl) {
207                 final PatternConstraint pattern = ((PatternEffectiveStatementImpl) effectiveStatement).argument();
208
209                 if (pattern != null) {
210                     patternConstraints.add(pattern);
211                 }
212             }
213         }
214
215         return !patternConstraints.isEmpty() ? ImmutableList.copyOf(patternConstraints) : Collections
216                 .<PatternConstraint> emptyList();
217     }
218
219     @Override
220     public TypeDefinition<?> getBaseType() {
221         return baseType;
222     }
223
224     @Override
225     public String getUnits() {
226         return units;
227     }
228
229     @Override
230     public Object getDefaultValue() {
231         return defaultValue;
232     }
233
234     @Override
235     public QName getQName() {
236         return qName;
237     }
238
239     @Override
240     public SchemaPath getPath() {
241         return path;
242     }
243
244     @Override
245     public List<UnknownSchemaNode> getUnknownSchemaNodes() {
246         return Collections.emptyList();
247     }
248
249     public List<RangeConstraint> getRangeConstraints() {
250         return ranges;
251     }
252
253     public List<LengthConstraint> getLengthConstraints() {
254         return lengths;
255     }
256
257     public List<PatternConstraint> getPatternConstraints() {
258         return patterns;
259     }
260
261     public Integer getFractionDigits() {
262         return fractionDigits;
263     }
264
265     @Override
266     public TypeDefinition<?> getTypeDefinition() {
267         if (extendedType != null) {
268             return extendedType;
269         }
270
271         if (!isExtended && baseType instanceof TypeDefEffectiveStatementImpl) {
272             TypeDefEffectiveStatementImpl originalTypeDef = (TypeDefEffectiveStatementImpl) baseType;
273             return originalTypeDef.getTypeDefinition();
274         }
275
276         Builder extendedTypeBuilder;
277         if (baseType instanceof TypeDefEffectiveStatementImpl) {
278             TypeDefEffectiveStatementImpl typeDefBaseType = (TypeDefEffectiveStatementImpl) baseType;
279             extendedTypeBuilder = ExtendedType.builder(qName, typeDefBaseType.getTypeDefinition(),
280                     Optional.fromNullable(getDescription()), Optional.fromNullable(getReference()), path);
281         } else {
282             extendedTypeBuilder = ExtendedType.builder(qName, baseType, Optional.fromNullable(getDescription()),
283                     Optional.fromNullable(getReference()), path);
284         }
285
286         extendedTypeBuilder.fractionDigits(fractionDigits);
287         extendedTypeBuilder.ranges(ranges);
288         extendedTypeBuilder.lengths(lengths);
289         extendedTypeBuilder.patterns(patterns);
290
291         extendedType = extendedTypeBuilder.build();
292
293         return extendedType;
294     }
295 }