Added export of augmentation schemas to Binding Context
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / util / ParserListenerUtils.java
1 /*
2  * Copyright (c) 2013 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/eplv10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.util;
9
10 import java.net.URI;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.Date;
14 import java.util.List;
15 import java.util.Stack;
16
17 import org.antlr.v4.runtime.tree.ParseTree;
18 import org.opendaylight.yangtools.antlrv4.code.gen.*;
19 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext;
20 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext;
21 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Bit_stmtContext;
22 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Bits_specificationContext;
23 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Config_argContext;
24 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Config_stmtContext;
25 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Decimal64_specificationContext;
26 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Default_stmtContext;
27 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Description_stmtContext;
28 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Enum_specificationContext;
29 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Enum_stmtContext;
30 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Error_app_tag_stmtContext;
31 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Error_message_stmtContext;
32 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Fraction_digits_stmtContext;
33 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Identityref_specificationContext;
34 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Instance_identifier_specificationContext;
35 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Leafref_specificationContext;
36 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Length_stmtContext;
37 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Mandatory_argContext;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Mandatory_stmtContext;
39 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Max_elements_stmtContext;
40 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Max_value_argContext;
41 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Min_elements_stmtContext;
42 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Min_value_argContext;
43 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Must_stmtContext;
44 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Numerical_restrictionsContext;
45 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Ordered_by_argContext;
46 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Ordered_by_stmtContext;
47 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Path_stmtContext;
48 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Pattern_stmtContext;
49 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Position_stmtContext;
50 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Presence_stmtContext;
51 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Range_stmtContext;
52 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Reference_stmtContext;
53 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_anyxml_stmtsContext;
54 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_choice_stmtsContext;
55 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_container_stmtsContext;
56 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_leaf_list_stmtsContext;
57 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_leaf_stmtsContext;
58 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_list_stmtsContext;
59 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_pomContext;
60 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_stmtContext;
61 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Require_instance_argContext;
62 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Require_instance_stmtContext;
63 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Status_argContext;
64 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Status_stmtContext;
65 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.StringContext;
66 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.String_restrictionsContext;
67 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Type_body_stmtsContext;
68 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Units_stmtContext;
69 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Value_stmtContext;
70 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.When_stmtContext;
71 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yin_element_argContext;
72 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yin_element_stmtContext;
73 import org.opendaylight.yangtools.yang.common.QName;
74 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
75 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
76 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
77 import org.opendaylight.yangtools.yang.model.api.Status;
78 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
79 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
80 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
81 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
82 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
83 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
84 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
85 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
86 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
87 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
88 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
89 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
90 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
91 import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
92 import org.opendaylight.yangtools.yang.model.util.BaseTypes;
93 import org.opendaylight.yangtools.yang.model.util.BinaryType;
94 import org.opendaylight.yangtools.yang.model.util.BitsType;
95 import org.opendaylight.yangtools.yang.model.util.Decimal64;
96 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
97 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
98 import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
99 import org.opendaylight.yangtools.yang.model.util.Int16;
100 import org.opendaylight.yangtools.yang.model.util.Int32;
101 import org.opendaylight.yangtools.yang.model.util.Int64;
102 import org.opendaylight.yangtools.yang.model.util.Int8;
103 import org.opendaylight.yangtools.yang.model.util.Leafref;
104 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
105 import org.opendaylight.yangtools.yang.model.util.StringType;
106 import org.opendaylight.yangtools.yang.model.util.Uint16;
107 import org.opendaylight.yangtools.yang.model.util.Uint32;
108 import org.opendaylight.yangtools.yang.model.util.Uint64;
109 import org.opendaylight.yangtools.yang.model.util.Uint8;
110 import org.opendaylight.yangtools.yang.model.util.UnknownType;
111 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
112 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
113 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
114 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
115 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
116 import org.opendaylight.yangtools.yang.parser.builder.impl.ConstraintsBuilder;
117 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
118 import org.slf4j.Logger;
119 import org.slf4j.LoggerFactory;
120
121 public final class ParserListenerUtils {
122     private static final Logger LOG = LoggerFactory.getLogger(ParserListenerUtils.class);
123
124     private ParserListenerUtils() {
125     }
126
127     /**
128      * Parse given tree and get first string value.
129      *
130      * @param treeNode
131      *            tree to parse
132      * @return first string value from given tree
133      */
134     public static String stringFromNode(final ParseTree treeNode) {
135         final String result = "";
136         for (int i = 0; i < treeNode.getChildCount(); ++i) {
137             if (treeNode.getChild(i) instanceof StringContext) {
138                 final StringContext context = (StringContext) treeNode.getChild(i);
139                 if (context != null) {
140                     return context.getChild(0).getText().replace("\"", "");
141                 }
142             }
143         }
144         return result;
145     }
146
147     /**
148      * Parse 'description', 'reference' and 'status' statements and fill in
149      * given builder.
150      *
151      * @param ctx
152      *            context to parse
153      * @param builder
154      *            builder to fill in with parsed statements
155      */
156     public static void parseSchemaNodeArgs(final ParseTree ctx, final SchemaNodeBuilder builder) {
157         for (int i = 0; i < ctx.getChildCount(); i++) {
158             final ParseTree child = ctx.getChild(i);
159             if (child instanceof Description_stmtContext) {
160                 final String desc = stringFromNode(child);
161                 builder.setDescription(desc);
162             } else if (child instanceof Reference_stmtContext) {
163                 final String ref = stringFromNode(child);
164                 builder.setReference(ref);
165             } else if (child instanceof Status_stmtContext) {
166                 final Status status = parseStatus((Status_stmtContext) child);
167                 builder.setStatus(status);
168             }
169         }
170     }
171
172     /**
173      * Parse given context and return its value;
174      *
175      * @param ctx
176      *            status context
177      * @return value parsed from context
178      */
179     public static Status parseStatus(final Status_stmtContext ctx) {
180         Status result = null;
181         for (int i = 0; i < ctx.getChildCount(); i++) {
182             ParseTree statusArg = ctx.getChild(i);
183             if (statusArg instanceof Status_argContext) {
184                 String statusArgStr = stringFromNode(statusArg);
185                 switch (statusArgStr) {
186                 case "current":
187                     result = Status.CURRENT;
188                     break;
189                 case "deprecated":
190                     result = Status.DEPRECATED;
191                     break;
192                 case "obsolete":
193                     result = Status.OBSOLETE;
194                     break;
195                 default:
196                     LOG.warn("Invalid 'status' statement: " + statusArgStr);
197                 }
198             }
199         }
200         return result;
201     }
202
203     /**
204      * Parse given tree and returns units statement as string.
205      *
206      * @param ctx
207      *            context to parse
208      * @return value of units statement as string or null if there is no units
209      *         statement
210      */
211     public static String parseUnits(final ParseTree ctx) {
212         String units = null;
213         for (int i = 0; i < ctx.getChildCount(); i++) {
214             ParseTree child = ctx.getChild(i);
215             if (child instanceof Units_stmtContext) {
216                 units = stringFromNode(child);
217                 break;
218             }
219         }
220         return units;
221     }
222
223     /**
224      * Parse given tree and returns default statement as string.
225      *
226      * @param ctx
227      *            context to parse
228      * @return value of default statement as string or null if there is no
229      *         default statement
230      */
231     public static String parseDefault(final ParseTree ctx) {
232         String defaultValue = null;
233         for (int i = 0; i < ctx.getChildCount(); i++) {
234             ParseTree child = ctx.getChild(i);
235             if (child instanceof Default_stmtContext) {
236                 defaultValue = stringFromNode(child);
237                 break;
238             }
239         }
240         return defaultValue;
241     }
242
243     /**
244      * Create SchemaPath from actualPath and new node name.
245      *
246      * @param actualPath
247      *            current position in model
248      * @return SchemaPath object
249      */
250     public static SchemaPath createActualSchemaPath(final Stack<QName> actualPath) {
251         final List<QName> path = new ArrayList<>(actualPath);
252         return new SchemaPath(path, true);
253     }
254
255     /**
256      * Create java.util.List of QName objects from given key definition as
257      * string.
258      *
259      * @param keyDefinition
260      *            key definition as string
261      * @param namespace
262      *            current namespace
263      * @param revision
264      *            current revision
265      * @param prefix
266      *            current prefix
267      * @return YANG list key as java.util.List of QName objects
268      */
269     public static List<QName> createListKey(final String keyDefinition, final URI namespace, final Date revision,
270             final String prefix) {
271         List<QName> key = new ArrayList<>();
272         String[] splittedKey = keyDefinition.split(" ");
273
274         QName qname;
275         for (String keyElement : splittedKey) {
276             if (keyElement.length() != 0) {
277                 qname = new QName(namespace, revision, prefix, keyElement);
278                 key.add(qname);
279             }
280         }
281         return key;
282     }
283
284     /**
285      * Parse given type body of enumeration statement.
286      *
287      * @param ctx
288      *            type body context to parse
289      * @param path
290      *            actual position in YANG model
291      * @param moduleName
292      *            current module name
293      * @return List of EnumPair object parsed from given context
294      */
295     private static List<EnumTypeDefinition.EnumPair> getEnumConstants(final Type_body_stmtsContext ctx,
296             final Stack<QName> path, final String moduleName) {
297         List<EnumTypeDefinition.EnumPair> enumConstants = new ArrayList<>();
298
299         for (int i = 0; i < ctx.getChildCount(); i++) {
300             ParseTree enumSpecChild = ctx.getChild(i);
301             if (enumSpecChild instanceof Enum_specificationContext) {
302                 int highestValue = -1;
303                 for (int j = 0; j < enumSpecChild.getChildCount(); j++) {
304                     ParseTree enumChild = enumSpecChild.getChild(j);
305                     if (enumChild instanceof Enum_stmtContext) {
306                         EnumPair enumPair = createEnumPair((Enum_stmtContext) enumChild, highestValue, path, moduleName);
307                         if (enumPair.getValue() > highestValue) {
308                             highestValue = enumPair.getValue();
309                         }
310                         enumConstants.add(enumPair);
311                     }
312                 }
313             }
314         }
315         return enumConstants;
316     }
317
318     /**
319      * Parse enum statement context
320      *
321      * @param ctx
322      *            enum statement context
323      * @param highestValue
324      *            current highest value in enumeration
325      * @param actualPath
326      *            actual position in YANG model
327      * @param moduleName
328      *            current module name
329      * @return EnumPair object parsed from given context
330      */
331     private static EnumTypeDefinition.EnumPair createEnumPair(final Enum_stmtContext ctx, final int highestValue,
332             final Stack<QName> actualPath, final String moduleName) {
333         final String name = stringFromNode(ctx);
334         SchemaPath path = createTypePath(actualPath, name);
335         Integer value = null;
336
337         String description = null;
338         String reference = null;
339         Status status = null;
340
341         for (int i = 0; i < ctx.getChildCount(); i++) {
342             ParseTree child = ctx.getChild(i);
343             if (child instanceof Value_stmtContext) {
344                 String valueStr = stringFromNode(child);
345                 value = Integer.valueOf(valueStr);
346             } else if (child instanceof Description_stmtContext) {
347                 description = stringFromNode(child);
348             } else if (child instanceof Reference_stmtContext) {
349                 reference = stringFromNode(child);
350             } else if (child instanceof Status_stmtContext) {
351                 status = parseStatus((Status_stmtContext) child);
352             }
353         }
354
355         if (value == null) {
356             value = highestValue + 1;
357         }
358         if (value < -2147483648 || value > 2147483647) {
359             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Error on enum '" + name
360                     + "': the enum value MUST be in the range from -2147483648 to 2147483647, but was: " + value);
361         }
362
363         EnumPairImpl result = new EnumPairImpl();
364         result.qname = path.getPath().get(path.getPath().size() - 1);
365         result.path = path;
366         result.description = description;
367         result.reference = reference;
368         result.status = status;
369         result.name = name;
370         result.value = value;
371         return result;
372     }
373
374     /**
375      * Internal implementation of EnumPair.
376      */
377     private static class EnumPairImpl implements EnumTypeDefinition.EnumPair {
378         private QName qname;
379         private SchemaPath path;
380         private String description;
381         private String reference;
382         private Status status;
383         private List<UnknownSchemaNode> unknownNodes = Collections.emptyList();
384         private String name;
385         private Integer value;
386
387         @Override
388         public QName getQName() {
389             return qname;
390         }
391
392         @Override
393         public SchemaPath getPath() {
394             return path;
395         }
396
397         @Override
398         public String getDescription() {
399             return description;
400         }
401
402         @Override
403         public String getReference() {
404             return reference;
405         }
406
407         @Override
408         public Status getStatus() {
409             return status;
410         }
411
412         @Override
413         public List<UnknownSchemaNode> getUnknownSchemaNodes() {
414             return unknownNodes;
415         }
416
417         @Override
418         public String getName() {
419             return name;
420         }
421
422         @Override
423         public Integer getValue() {
424             return value;
425         }
426
427         @Override
428         public int hashCode() {
429             final int prime = 31;
430             int result = 1;
431             result = prime * result + ((qname == null) ? 0 : qname.hashCode());
432             result = prime * result + ((path == null) ? 0 : path.hashCode());
433             result = prime * result + ((unknownNodes == null) ? 0 : unknownNodes.hashCode());
434             result = prime * result + ((name == null) ? 0 : name.hashCode());
435             result = prime * result + ((value == null) ? 0 : value.hashCode());
436             return result;
437         }
438
439         @Override
440         public boolean equals(Object obj) {
441             if (this == obj) {
442                 return true;
443             }
444             if (obj == null) {
445                 return false;
446             }
447             if (getClass() != obj.getClass()) {
448                 return false;
449             }
450             EnumPairImpl other = (EnumPairImpl) obj;
451             if (qname == null) {
452                 if (other.qname != null) {
453                     return false;
454                 }
455             } else if (!qname.equals(other.qname)) {
456                 return false;
457             }
458             if (path == null) {
459                 if (other.path != null) {
460                     return false;
461                 }
462             } else if (!path.equals(other.path)) {
463                 return false;
464             }
465             if (unknownNodes == null) {
466                 if (other.unknownNodes != null) {
467                     return false;
468                 }
469             } else if (!unknownNodes.equals(other.unknownNodes)) {
470                 return false;
471             }
472             if (name == null) {
473                 if (other.name != null) {
474                     return false;
475                 }
476             } else if (!name.equals(other.name)) {
477                 return false;
478             }
479             if (value == null) {
480                 if (other.value != null) {
481                     return false;
482                 }
483             } else if (!value.equals(other.value)) {
484                 return false;
485             }
486             return true;
487         }
488
489         @Override
490         public String toString() {
491             return EnumTypeDefinition.EnumPair.class.getSimpleName() + "[name=" + name + ", value=" + value + "]";
492         }
493     }
494
495     /**
496      * Get and parse range from given type body context.
497      *
498      * @param ctx
499      *            type body context to parse
500      * @param moduleName
501      *            name of current module
502      * @return List of RangeConstraint created from this context
503      */
504     private static List<RangeConstraint> getRangeConstraints(final Type_body_stmtsContext ctx, final String moduleName) {
505         List<RangeConstraint> rangeConstraints = Collections.emptyList();
506         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
507             ParseTree numRestrChild = ctx.getChild(i);
508             if (numRestrChild instanceof Numerical_restrictionsContext) {
509                 for (int j = 0; j < numRestrChild.getChildCount(); j++) {
510                     ParseTree rangeChild = numRestrChild.getChild(j);
511                     if (rangeChild instanceof Range_stmtContext) {
512                         rangeConstraints = parseRangeConstraints((Range_stmtContext) rangeChild, moduleName);
513                         break outer;
514                     }
515                 }
516             }
517         }
518         return rangeConstraints;
519     }
520
521     /**
522      * Parse given range context.
523      *
524      * @param ctx
525      *            range context to parse
526      * @param moduleName
527      *            name of current module
528      * @return List of RangeConstraints parsed from this context
529      */
530     private static List<RangeConstraint> parseRangeConstraints(final Range_stmtContext ctx, final String moduleName) {
531         final int line = ctx.getStart().getLine();
532         List<RangeConstraint> rangeConstraints = new ArrayList<>();
533         String description = null;
534         String reference = null;
535
536         for (int i = 0; i < ctx.getChildCount(); i++) {
537             ParseTree child = ctx.getChild(i);
538             if (child instanceof Description_stmtContext) {
539                 description = stringFromNode(child);
540             } else if (child instanceof Reference_stmtContext) {
541                 reference = stringFromNode(child);
542             }
543         }
544
545         String rangeStr = stringFromNode(ctx);
546         String trimmed = rangeStr.replace(" ", "");
547         String[] splittedRange = trimmed.split("\\|");
548         for (String rangeDef : splittedRange) {
549             String[] splittedRangeDef = rangeDef.split("\\.\\.");
550             Number min;
551             Number max;
552             if (splittedRangeDef.length == 1) {
553                 min = max = parseNumberConstraintValue(splittedRangeDef[0], moduleName, line);
554             } else {
555                 min = parseNumberConstraintValue(splittedRangeDef[0], moduleName, line);
556                 max = parseNumberConstraintValue(splittedRangeDef[1], moduleName, line);
557             }
558             RangeConstraint range = BaseConstraints.rangeConstraint(min, max, description, reference);
559             rangeConstraints.add(range);
560         }
561
562         return rangeConstraints;
563     }
564
565     /**
566      * Get and parse length from given type body context.
567      *
568      * @param ctx
569      *            type body context to parse
570      * @param moduleName
571      *            name of current module
572      * @return List of LengthConstraint created from this context
573      */
574     private static List<LengthConstraint> getLengthConstraints(final Type_body_stmtsContext ctx, final String moduleName) {
575         List<LengthConstraint> lengthConstraints = Collections.emptyList();
576         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
577             ParseTree stringRestrChild = ctx.getChild(i);
578             if (stringRestrChild instanceof String_restrictionsContext) {
579                 for (int j = 0; j < stringRestrChild.getChildCount(); j++) {
580                     ParseTree lengthChild = stringRestrChild.getChild(j);
581                     if (lengthChild instanceof Length_stmtContext) {
582                         lengthConstraints = parseLengthConstraints((Length_stmtContext) lengthChild, moduleName);
583                         break outer;
584                     }
585                 }
586             }
587         }
588         return lengthConstraints;
589     }
590
591     /**
592      * Parse given length context.
593      *
594      * @param ctx
595      *            length context to parse
596      * @param moduleName
597      *            name of current module
598      * @return List of LengthConstraints parsed from this context
599      */
600     private static List<LengthConstraint> parseLengthConstraints(final Length_stmtContext ctx, final String moduleName) {
601         final int line = ctx.getStart().getLine();
602         List<LengthConstraint> lengthConstraints = new ArrayList<>();
603         String description = null;
604         String reference = null;
605
606         for (int i = 0; i < ctx.getChildCount(); i++) {
607             ParseTree child = ctx.getChild(i);
608             if (child instanceof Description_stmtContext) {
609                 description = stringFromNode(child);
610             } else if (child instanceof Reference_stmtContext) {
611                 reference = stringFromNode(child);
612             }
613         }
614
615         String lengthStr = stringFromNode(ctx);
616         String trimmed = lengthStr.replace(" ", "");
617         String[] splittedRange = trimmed.split("\\|");
618         for (String rangeDef : splittedRange) {
619             String[] splittedRangeDef = rangeDef.split("\\.\\.");
620             Number min;
621             Number max;
622             if (splittedRangeDef.length == 1) {
623                 min = max = parseNumberConstraintValue(splittedRangeDef[0], moduleName, line);
624             } else {
625                 min = parseNumberConstraintValue(splittedRangeDef[0], moduleName, line);
626                 max = parseNumberConstraintValue(splittedRangeDef[1], moduleName, line);
627             }
628             LengthConstraint range = BaseConstraints.lengthConstraint(min, max, description, reference);
629             lengthConstraints.add(range);
630         }
631
632         return lengthConstraints;
633     }
634
635     /**
636      * @param value
637      *            value to parse
638      * @param moduleName
639      *            name of current module
640      * @param line
641      *            current line in module
642      * @return wrapper object of primitive java type or UnknownBoundaryNumber if
643      *         type is one of special YANG values 'min' or 'max'
644      */
645     private static Number parseNumberConstraintValue(final String value, final String moduleName, final int line) {
646         Number result;
647         if ("min".equals(value) || "max".equals(value)) {
648             result = new UnknownBoundaryNumber(value);
649         } else {
650             try {
651                 result = Long.valueOf(value);
652             } catch (NumberFormatException e) {
653                 throw new YangParseException(moduleName, line, "Unable to parse range value '" + value + "'.", e);
654             }
655         }
656         return result;
657     }
658
659     /**
660      * Parse type body and return pattern constraints.
661      *
662      * @param ctx
663      *            type body
664      * @return list of pattern constraints
665      */
666     private static List<PatternConstraint> getPatternConstraint(final Type_body_stmtsContext ctx) {
667         List<PatternConstraint> patterns = new ArrayList<>();
668
669         for (int i = 0; i < ctx.getChildCount(); i++) {
670             ParseTree stringRestrChild = ctx.getChild(i);
671             if (stringRestrChild instanceof String_restrictionsContext) {
672                 for (int j = 0; j < stringRestrChild.getChildCount(); j++) {
673                     ParseTree lengthChild = stringRestrChild.getChild(j);
674                     if (lengthChild instanceof Pattern_stmtContext) {
675                         patterns.add(parsePatternConstraint((Pattern_stmtContext) lengthChild));
676                     }
677                 }
678             }
679         }
680         return patterns;
681     }
682
683     /**
684      * Internal helper method.
685      *
686      * @param ctx
687      *            pattern context
688      * @return PatternConstraint object
689      */
690     private static PatternConstraint parsePatternConstraint(final Pattern_stmtContext ctx) {
691         String description = null;
692         String reference = null;
693         for (int i = 0; i < ctx.getChildCount(); i++) {
694             ParseTree child = ctx.getChild(i);
695             if (child instanceof Description_stmtContext) {
696                 description = stringFromNode(child);
697             } else if (child instanceof Reference_stmtContext) {
698                 reference = stringFromNode(child);
699             }
700         }
701         String pattern = patternStringFromNode(ctx);
702         return BaseConstraints.patternConstraint(pattern, description, reference);
703     }
704
705     /**
706      * Parse given context and return pattern value.
707      *
708      * @param ctx
709      *            context to parse
710      * @return pattern value as String
711      */
712     public static String patternStringFromNode(final Pattern_stmtContext ctx) {
713         StringBuilder result = new StringBuilder();
714         for (int i = 0; i < ctx.getChildCount(); ++i) {
715             ParseTree child = ctx.getChild(i);
716             if (child instanceof StringContext) {
717                 for (int j = 0; j < child.getChildCount(); j++) {
718                     if (j % 2 == 0) {
719                         String patternToken = child.getChild(j).getText();
720                         result.append(patternToken.substring(1, patternToken.length() - 1));
721                     }
722                 }
723             }
724         }
725         return result.toString();
726     }
727
728     /**
729      * Get fraction digits value from type body.
730      *
731      * @param ctx
732      *            type body context to parse
733      * @param moduleName
734      *            name of current module
735      * @return 'fraction-digits' value if present in given context, null
736      *         otherwise
737      */
738     private static Integer getFractionDigits(Type_body_stmtsContext ctx, String moduleName) {
739         Integer result = null;
740         for (int i = 0; i < ctx.getChildCount(); i++) {
741             ParseTree dec64specChild = ctx.getChild(i);
742             if (dec64specChild instanceof Decimal64_specificationContext) {
743                 result = parseFractionDigits((Decimal64_specificationContext) dec64specChild, moduleName);
744             }
745         }
746         return result;
747     }
748
749     /**
750      * Parse decimal64 fraction-digits value.
751      *
752      * @param ctx
753      *            decimal64 context
754      * @param moduleName
755      *            name of current module
756      * @return fraction-digits value as Integer
757      */
758     private static Integer parseFractionDigits(Decimal64_specificationContext ctx, String moduleName) {
759         Integer result = null;
760         for (int i = 0; i < ctx.getChildCount(); i++) {
761             ParseTree fdChild = ctx.getChild(i);
762             if (fdChild instanceof Fraction_digits_stmtContext) {
763                 String value = stringFromNode(fdChild);
764                 try {
765                     result = Integer.valueOf(value);
766                 } catch (NumberFormatException e) {
767                     throw new YangParseException(moduleName, ctx.getStart().getLine(),
768                             "Unable to parse fraction digits value '" + value + "'.", e);
769                 }
770             }
771         }
772         return result;
773     }
774
775     /**
776      * Internal helper method for parsing bit statements from given type body
777      * context.
778      *
779      * @param ctx
780      *            type body context to parse
781      * @param actualPath
782      *            current position in YANG model
783      * @param moduleName
784      *            current module name
785      * @return List of Bit objects created from this context
786      */
787     private static List<BitsTypeDefinition.Bit> getBits(Type_body_stmtsContext ctx, Stack<QName> actualPath,
788             String moduleName) {
789         final List<BitsTypeDefinition.Bit> bits = new ArrayList<>();
790         for (int j = 0; j < ctx.getChildCount(); j++) {
791             ParseTree bitsSpecChild = ctx.getChild(j);
792             if (bitsSpecChild instanceof Bits_specificationContext) {
793                 long highestPosition = -1;
794                 for (int k = 0; k < bitsSpecChild.getChildCount(); k++) {
795                     ParseTree bitChild = bitsSpecChild.getChild(k);
796                     if (bitChild instanceof Bit_stmtContext) {
797                         Bit bit = parseBit((Bit_stmtContext) bitChild, highestPosition, actualPath, moduleName);
798                         if (bit.getPosition() > highestPosition) {
799                             highestPosition = bit.getPosition();
800                         }
801                         bits.add(bit);
802                     }
803                 }
804             }
805         }
806         return bits;
807     }
808
809     /**
810      * Internal helper method for parsing bit context.
811      *
812      * @param ctx
813      *            bit statement context to parse
814      * @param highestPosition
815      *            current highest position in bits type
816      * @param actualPath
817      *            current position in YANG model
818      * @param moduleName
819      *            current module name
820      * @return Bit object parsed from this context
821      */
822     private static BitsTypeDefinition.Bit parseBit(final Bit_stmtContext ctx, long highestPosition,
823             Stack<QName> actualPath, final String moduleName) {
824         String name = stringFromNode(ctx);
825         Long position = null;
826
827         String description = null;
828         String reference = null;
829         Status status = Status.CURRENT;
830
831         SchemaPath schemaPath = createBaseTypePath(actualPath, name);
832
833         for (int i = 0; i < ctx.getChildCount(); i++) {
834             ParseTree child = ctx.getChild(i);
835             if (child instanceof Position_stmtContext) {
836                 String positionStr = stringFromNode(child);
837                 position = Long.valueOf(positionStr);
838             } else if (child instanceof Description_stmtContext) {
839                 description = stringFromNode(child);
840             } else if (child instanceof Reference_stmtContext) {
841                 reference = stringFromNode(child);
842             } else if (child instanceof Status_stmtContext) {
843                 status = parseStatus((Status_stmtContext) child);
844             }
845         }
846
847         if (position == null) {
848             position = highestPosition + 1;
849         }
850         if (position < 0 || position > 4294967295L) {
851             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Error on bit '" + name
852                     + "': the position value MUST be in the range 0 to 4294967295");
853         }
854
855         final List<UnknownSchemaNode> unknownNodes = Collections.emptyList();
856         return new BitImpl(position, schemaPath.getPath().get(schemaPath.getPath().size() - 1), schemaPath,
857                 description, reference, status, unknownNodes);
858     }
859
860     /**
861      * Parse 'ordered-by' statement.
862      *
863      * The 'ordered-by' statement defines whether the order of entries within a
864      * list are determined by the user or the system. The argument is one of the
865      * strings "system" or "user". If not present, order defaults to "system".
866      *
867      * @param ctx
868      *            Ordered_by_stmtContext
869      * @return true, if ordered-by contains value 'user', false otherwise
870      */
871     public static boolean parseUserOrdered(Ordered_by_stmtContext ctx) {
872         boolean result = false;
873         for (int j = 0; j < ctx.getChildCount(); j++) {
874             ParseTree orderArg = ctx.getChild(j);
875             if (orderArg instanceof Ordered_by_argContext) {
876                 String orderStr = stringFromNode(orderArg);
877                 switch (orderStr) {
878                 case "system":
879                     result = false;
880                     break;
881                 case "user":
882                     result = true;
883                     break;
884                 default:
885                     LOG.warn("Invalid 'ordered-by' statement.");
886                 }
887             }
888         }
889         return result;
890     }
891
892     /**
893      * Get config statement from given context. If there is no config statement,
894      * return config value of parent
895      *
896      * @param ctx
897      *            context to parse
898      * @param parent
899      *            parent node
900      * @param moduleName
901      *            name of current module
902      * @param line
903      *            line in current module
904      * @return config statement parsed from given context
905      */
906     public static Boolean getConfig(final ParseTree ctx, final Builder parent, final String moduleName, final int line) {
907         Boolean result;
908         // parse configuration statement
909         Boolean config = null;
910         for (int i = 0; i < ctx.getChildCount(); i++) {
911             ParseTree child = ctx.getChild(i);
912             if (child instanceof Config_stmtContext) {
913                 config = parseConfig((Config_stmtContext) child, moduleName);
914                 break;
915             }
916         }
917
918         // If 'config' is not specified, the default is the same as the parent
919         // schema node's 'config' value
920         if (config == null) {
921             if (parent instanceof DataSchemaNodeBuilder) {
922                 Boolean parentConfig = ((DataSchemaNodeBuilder) parent).isConfiguration();
923                 // If the parent node is a rpc input or output, it can has
924                 // config set to null
925                 result = parentConfig == null ? true : parentConfig;
926             } else {
927                 result = true;
928             }
929         } else {
930             // Check first: if a node has 'config' set to 'false', no node
931             // underneath it can have 'config' set to 'true'
932             if (parent instanceof DataSchemaNodeBuilder && !(parent instanceof ChoiceCaseBuilder)) {
933                 Boolean parentConfig = ((DataSchemaNodeBuilder) parent).isConfiguration();
934                 if (!parentConfig && config) {
935                     throw new YangParseException(moduleName, line,
936                             "Can not set 'config' to 'true' if parent node has 'config' set to 'false'");
937                 }
938             }
939             result = config;
940         }
941
942         return result;
943     }
944
945     /**
946      * Parse config statement.
947      *
948      * @param ctx
949      *            config context to parse
950      * @param moduleName
951      *            current module name
952      * @return true if given context contains string 'true', false otherwise
953      */
954     private static Boolean parseConfig(final Config_stmtContext ctx, final String moduleName) {
955         Boolean result = null;
956         if (ctx != null) {
957             for (int i = 0; i < ctx.getChildCount(); ++i) {
958                 final ParseTree configContext = ctx.getChild(i);
959                 if (configContext instanceof Config_argContext) {
960                     final String value = stringFromNode(configContext);
961                     switch (value) {
962                     case "true":
963                         result = true;
964                         break;
965                     case "false":
966                         result = false;
967                         break;
968                     default:
969                         throw new YangParseException(moduleName, ctx.getStart().getLine(),
970                                 "Failed to parse 'config' statement value: '" + value + "'.");
971                     }
972                 }
973             }
974         }
975         return result;
976     }
977
978     /**
979      * Parse type body and create UnknownType definition.
980      *
981      * @param typedefQName
982      *            qname of current type
983      * @param ctx
984      *            type body
985      * @param actualPath
986      *            actual path in model
987      * @param namespace
988      *            module namespace
989      * @param revision
990      *            module revision
991      * @param prefix
992      *            module prefix
993      * @param parent
994      *            current node parent
995      * @return UnknownType object with constraints from parsed type body
996      */
997     public static TypeDefinition<?> parseUnknownTypeWithBody(final QName typedefQName,
998             final Type_body_stmtsContext ctx, final Stack<QName> actualPath, final URI namespace, final Date revision,
999             final String prefix, final Builder parent) {
1000         String moduleName = parent.getModuleName();
1001         String typeName = typedefQName.getLocalName();
1002
1003         UnknownType.Builder unknownType = new UnknownType.Builder(typedefQName);
1004
1005         if (ctx != null) {
1006             List<RangeConstraint> rangeStatements = getRangeConstraints(ctx, moduleName);
1007             List<LengthConstraint> lengthStatements = getLengthConstraints(ctx, moduleName);
1008             List<PatternConstraint> patternStatements = getPatternConstraint(ctx);
1009             Integer fractionDigits = getFractionDigits(ctx, moduleName);
1010
1011             if (parent instanceof TypeDefinitionBuilder) {
1012                 TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
1013                 typedef.setRanges(rangeStatements);
1014                 typedef.setLengths(lengthStatements);
1015                 typedef.setPatterns(patternStatements);
1016                 typedef.setFractionDigits(fractionDigits);
1017                 return unknownType.build();
1018             } else {
1019                 TypeDefinition<?> baseType = unknownType.build();
1020                 QName qname = new QName(namespace, revision, prefix, typeName);
1021                 SchemaPath schemaPath = createTypePath(actualPath, typeName);
1022
1023                 ExtendedType.Builder typeBuilder = new ExtendedType.Builder(qname, baseType, null, null, schemaPath);
1024                 typeBuilder.ranges(rangeStatements);
1025                 typeBuilder.lengths(lengthStatements);
1026                 typeBuilder.patterns(patternStatements);
1027                 typeBuilder.fractionDigits(fractionDigits);
1028
1029                 return typeBuilder.build();
1030             }
1031         }
1032
1033         return unknownType.build();
1034     }
1035
1036     /**
1037      * Create TypeDefinition object based on given type name and type body.
1038      *
1039      * @param typeName
1040      *            name of type
1041      * @param typeBody
1042      *            type body context
1043      * @param actualPath
1044      *            current path in schema
1045      * @param namespace
1046      *            current namespace
1047      * @param revision
1048      *            current revision
1049      * @param prefix
1050      *            current prefix
1051      * @param parent
1052      *            parent builder
1053      * @return TypeDefinition object based on parsed values.
1054      */
1055     public static TypeDefinition<?> parseTypeWithBody(final String typeName, final Type_body_stmtsContext typeBody,
1056             final Stack<QName> actualPath, final URI namespace, final Date revision, final String prefix,
1057             final Builder parent) {
1058
1059         final String moduleName = parent.getModuleName();
1060         final int line = typeBody.getStart().getLine();
1061         TypeDefinition<?> baseType = null;
1062
1063         Integer fractionDigits = getFractionDigits(typeBody, moduleName);
1064         List<LengthConstraint> lengthStatements = getLengthConstraints(typeBody, moduleName);
1065         List<PatternConstraint> patternStatements = getPatternConstraint(typeBody);
1066         List<RangeConstraint> rangeStatements = getRangeConstraints(typeBody, moduleName);
1067
1068         TypeConstraints constraints = new TypeConstraints(moduleName, line);
1069         constraints.addFractionDigits(fractionDigits);
1070         constraints.addLengths(lengthStatements);
1071         constraints.addPatterns(patternStatements);
1072         constraints.addRanges(rangeStatements);
1073
1074         SchemaPath baseTypePath = createBaseTypePath(actualPath, typeName);
1075         SchemaPath extBaseTypePath = createExtendedBaseTypePath(actualPath, namespace, revision, prefix, typeName);
1076
1077         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
1078             extBaseTypePath = baseTypePath;
1079         }
1080
1081         if ("decimal64".equals(typeName)) {
1082             if (rangeStatements.isEmpty()) {
1083                 try {
1084                     return new Decimal64(baseTypePath, fractionDigits);
1085                 } catch(Exception e) {
1086                     throw new YangParseException(moduleName, line, e.getMessage());
1087                 }
1088             }
1089             Decimal64 decimalType = new Decimal64(extBaseTypePath, fractionDigits);
1090             constraints.addRanges(decimalType.getRangeConstraints());
1091             baseType = decimalType;
1092         } else if (typeName.startsWith("int")) {
1093             IntegerTypeDefinition intType = null;
1094             switch (typeName) {
1095             case "int8":
1096                 intType = Int8.getInstance();
1097                 break;
1098             case "int16":
1099                 intType = Int16.getInstance();
1100                 break;
1101             case "int32":
1102                 intType = Int32.getInstance();
1103                 break;
1104             case "int64":
1105                 intType = Int64.getInstance();
1106                 break;
1107             }
1108             if (intType == null) {
1109                 throw new YangParseException(moduleName, line, "Unknown yang type " + typeName);
1110             }
1111             constraints.addRanges(intType.getRangeConstraints());
1112             baseType = intType;
1113         } else if (typeName.startsWith("uint")) {
1114             UnsignedIntegerTypeDefinition uintType = null;
1115             switch (typeName) {
1116             case "uint8":
1117                 uintType = Uint8.getInstance();
1118                 break;
1119             case "uint16":
1120                 uintType = Uint16.getInstance();
1121                 break;
1122             case "uint32":
1123                 uintType = Uint32.getInstance();
1124                 break;
1125             case "uint64":
1126                 uintType = Uint64.getInstance();
1127                 break;
1128             }
1129             if (uintType == null) {
1130                 throw new YangParseException(moduleName, line, "Unknown yang type " + typeName);
1131             }
1132             constraints.addRanges(uintType.getRangeConstraints());
1133             baseType = uintType;
1134         } else if ("enumeration".equals(typeName)) {
1135             List<EnumTypeDefinition.EnumPair> enumConstants = getEnumConstants(typeBody, actualPath, moduleName);
1136             return new EnumerationType(baseTypePath, enumConstants);
1137         } else if ("string".equals(typeName)) {
1138             StringTypeDefinition stringType = StringType.getIntance();
1139             constraints.addLengths(stringType.getLengthConstraints());
1140             baseType = stringType;
1141         } else if ("bits".equals(typeName)) {
1142             return new BitsType(baseTypePath, getBits(typeBody, actualPath, moduleName));
1143         } else if ("leafref".equals(typeName)) {
1144             final String path = parseLeafrefPath(typeBody);
1145             final boolean absolute = path.startsWith("/");
1146             RevisionAwareXPath xpath = new RevisionAwareXPathImpl(path, absolute);
1147             return new Leafref(xpath);
1148         } else if ("binary".equals(typeName)) {
1149             BinaryTypeDefinition binaryType = BinaryType.getInstance();
1150             constraints.addLengths(binaryType.getLengthConstraints());
1151             baseType = binaryType;
1152         } else if ("instance-identifier".equals(typeName)) {
1153             boolean requireInstance = isRequireInstance(typeBody);
1154             return new InstanceIdentifier(null, requireInstance);
1155         }
1156
1157         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
1158             TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
1159             typedef.setRanges(constraints.getRange());
1160             typedef.setLengths(constraints.getLength());
1161             typedef.setPatterns(constraints.getPatterns());
1162             typedef.setFractionDigits(constraints.getFractionDigits());
1163             return baseType;
1164         }
1165
1166         List<QName> path = new ArrayList<>(actualPath);
1167         path.add(new QName(namespace, revision, prefix, typeName));
1168         SchemaPath schemaPath = new SchemaPath(path, true);
1169
1170         QName qname = schemaPath.getPath().get(schemaPath.getPath().size() - 1);
1171         ExtendedType.Builder typeBuilder = new ExtendedType.Builder(qname, baseType, "", "", schemaPath);
1172
1173         typeBuilder.ranges(constraints.getRange());
1174         typeBuilder.lengths(constraints.getLength());
1175         typeBuilder.patterns(constraints.getPatterns());
1176         typeBuilder.fractionDigits(constraints.getFractionDigits());
1177
1178         return typeBuilder.build();
1179     }
1180
1181     private static SchemaPath createTypePath(Stack<QName> actual, String typeName) {
1182         QName last = actual.peek();
1183         QName typeQName = new QName(last.getNamespace(), last.getRevision(), last.getPrefix(), typeName);
1184         List<QName> path = new ArrayList<>(actual);
1185         path.add(typeQName);
1186         return new SchemaPath(path, true);
1187     }
1188
1189     private static SchemaPath createBaseTypePath(Stack<QName> actual, String typeName) {
1190         List<QName> path = new ArrayList<>(actual);
1191         path.add(BaseTypes.constructQName(typeName));
1192         return new SchemaPath(path, true);
1193     }
1194
1195     private static SchemaPath createExtendedBaseTypePath(Stack<QName> actual, URI namespace, Date revision,
1196             String prefix, String typeName) {
1197         QName extTypeName = new QName(namespace, revision, prefix, typeName);
1198         QName baseTypeName = BaseTypes.constructQName(typeName);
1199         List<QName> path = new ArrayList<>(actual);
1200         path.add(extTypeName);
1201         path.add(baseTypeName);
1202         return new SchemaPath(path, true);
1203     }
1204
1205     /**
1206      * Parse given context and find identityref base value.
1207      *
1208      * @param ctx
1209      *            type body
1210      * @return identityref base value as String
1211      */
1212     public static String getIdentityrefBase(Type_body_stmtsContext ctx) {
1213         String result = null;
1214         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
1215             ParseTree child = ctx.getChild(i);
1216             if (child instanceof Identityref_specificationContext) {
1217                 for (int j = 0; j < child.getChildCount(); j++) {
1218                     ParseTree baseArg = child.getChild(j);
1219                     if (baseArg instanceof Base_stmtContext) {
1220                         result = stringFromNode(baseArg);
1221                         break outer;
1222                     }
1223                 }
1224             }
1225         }
1226         return result;
1227     }
1228
1229     /**
1230      * Parse type body statement and find require-instance value.
1231      *
1232      * @param ctx
1233      *            type body context
1234      * @return require-instance value
1235      */
1236     private static boolean isRequireInstance(Type_body_stmtsContext ctx) {
1237         for (int i = 0; i < ctx.getChildCount(); i++) {
1238             ParseTree child = ctx.getChild(i);
1239             if (child instanceof Instance_identifier_specificationContext) {
1240                 for (int j = 0; j < child.getChildCount(); j++) {
1241                     ParseTree reqStmt = child.getChild(j);
1242                     if (reqStmt instanceof Require_instance_stmtContext) {
1243                         for (int k = 0; k < reqStmt.getChildCount(); k++) {
1244                             ParseTree reqArg = reqStmt.getChild(k);
1245                             if (reqArg instanceof Require_instance_argContext) {
1246                                 return Boolean.valueOf(stringFromNode(reqArg));
1247                             }
1248                         }
1249                     }
1250                 }
1251             }
1252         }
1253         return true;
1254     }
1255
1256     /**
1257      * Parse type body statement and find leafref path.
1258      *
1259      * @param ctx
1260      *            type body context
1261      * @return leafref path as String
1262      */
1263     private static String parseLeafrefPath(Type_body_stmtsContext ctx) {
1264         for (int i = 0; i < ctx.getChildCount(); i++) {
1265             ParseTree child = ctx.getChild(i);
1266             if (child instanceof Leafref_specificationContext) {
1267                 for (int j = 0; j < child.getChildCount(); j++) {
1268                     ParseTree leafRefSpec = child.getChild(j);
1269                     if (leafRefSpec instanceof Path_stmtContext) {
1270                         return stringFromNode(leafRefSpec);
1271                     }
1272                 }
1273             }
1274         }
1275         return null;
1276     }
1277
1278     /**
1279      * Internal helper method for parsing must statement.
1280      *
1281      * @param ctx
1282      *            Must_stmtContext
1283      * @return MustDefinition object based on parsed context
1284      */
1285     public static MustDefinition parseMust(final YangParser.Must_stmtContext ctx) {
1286         StringBuilder mustText = new StringBuilder();
1287         String description = null;
1288         String reference = null;
1289         String errorAppTag = null;
1290         String errorMessage = null;
1291         for (int i = 0; i < ctx.getChildCount(); ++i) {
1292             ParseTree child = ctx.getChild(i);
1293             if (child instanceof StringContext) {
1294                 final StringContext context = (StringContext) child;
1295                 if (context.getChildCount() == 1) {
1296                     String mustPart = context.getChild(0).getText();
1297                     // trim start and end quotation
1298                     mustText.append(mustPart.substring(1, mustPart.length() - 1));
1299                 } else {
1300                     for (int j = 0; j < context.getChildCount(); j++) {
1301                         String mustPart = context.getChild(j).getText();
1302                         if (j == 0) {
1303                             mustText.append(mustPart.substring(0, mustPart.length() - 1));
1304                             continue;
1305                         }
1306                         if (j % 2 == 0) {
1307                             mustText.append(mustPart.substring(1));
1308                         }
1309                     }
1310                 }
1311             } else if (child instanceof Description_stmtContext) {
1312                 description = stringFromNode(child);
1313             } else if (child instanceof Reference_stmtContext) {
1314                 reference = stringFromNode(child);
1315             } else if (child instanceof Error_app_tag_stmtContext) {
1316                 errorAppTag = stringFromNode(child);
1317             } else if (child instanceof Error_message_stmtContext) {
1318                 errorMessage = stringFromNode(child);
1319             }
1320         }
1321
1322         return new MustDefinitionImpl(mustText.toString(), description, reference, errorAppTag, errorMessage);
1323     }
1324
1325     /**
1326      * Parse given context and set constraints to constraints builder.
1327      *
1328      * @param ctx
1329      *            context to parse
1330      * @param constraints
1331      *            ConstraintsBuilder to fill
1332      */
1333     public static void parseConstraints(final ParseTree ctx, final ConstraintsBuilder constraints) {
1334         for (int i = 0; i < ctx.getChildCount(); ++i) {
1335             final ParseTree childNode = ctx.getChild(i);
1336             if (childNode instanceof Max_elements_stmtContext) {
1337                 Integer max = parseMaxElements((Max_elements_stmtContext) childNode, constraints.getModuleName());
1338                 constraints.setMaxElements(max);
1339             } else if (childNode instanceof Min_elements_stmtContext) {
1340                 Integer min = parseMinElements((Min_elements_stmtContext) childNode, constraints.getModuleName());
1341                 constraints.setMinElements(min);
1342             } else if (childNode instanceof Must_stmtContext) {
1343                 MustDefinition must = parseMust((Must_stmtContext) childNode);
1344                 constraints.addMustDefinition(must);
1345             } else if (childNode instanceof Mandatory_stmtContext) {
1346                 for (int j = 0; j < childNode.getChildCount(); j++) {
1347                     ParseTree mandatoryTree = childNode.getChild(j);
1348                     if (mandatoryTree instanceof Mandatory_argContext) {
1349                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1350                         constraints.setMandatory(mandatory);
1351                     }
1352                 }
1353             } else if (childNode instanceof When_stmtContext) {
1354                 constraints.addWhenCondition(stringFromNode(childNode));
1355             }
1356         }
1357     }
1358
1359     private static Integer parseMinElements(Min_elements_stmtContext ctx, String moduleName) {
1360         Integer result = null;
1361         try {
1362             for (int i = 0; i < ctx.getChildCount(); i++) {
1363                 ParseTree minArg = ctx.getChild(i);
1364                 if (minArg instanceof Min_value_argContext) {
1365                     result = Integer.valueOf(stringFromNode(minArg));
1366                 }
1367             }
1368             if (result == null) {
1369                 throw new IllegalArgumentException();
1370             }
1371             return result;
1372         } catch (Exception e) {
1373             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Failed to parse min-elements.", e);
1374         }
1375     }
1376
1377     private static Integer parseMaxElements(Max_elements_stmtContext ctx, String moduleName) {
1378         Integer result = null;
1379         try {
1380             for (int i = 0; i < ctx.getChildCount(); i++) {
1381                 ParseTree maxArg = ctx.getChild(i);
1382                 if (maxArg instanceof Max_value_argContext) {
1383                     result = Integer.valueOf(stringFromNode(maxArg));
1384                 }
1385             }
1386             if (result == null) {
1387                 throw new IllegalArgumentException();
1388             }
1389             return result;
1390         } catch (Exception e) {
1391             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Failed to parse max-elements.", e);
1392         }
1393     }
1394
1395     /**
1396      * Parse given context and return yin value.
1397      *
1398      * @param ctx
1399      *            context to parse
1400      * @return true if value is 'true', false otherwise
1401      */
1402     public static boolean parseYinValue(Argument_stmtContext ctx) {
1403         boolean yinValue = false;
1404         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
1405             ParseTree yin = ctx.getChild(i);
1406             if (yin instanceof Yin_element_stmtContext) {
1407                 for (int j = 0; j < yin.getChildCount(); j++) {
1408                     ParseTree yinArg = yin.getChild(j);
1409                     if (yinArg instanceof Yin_element_argContext) {
1410                         String yinString = stringFromNode(yinArg);
1411                         if ("true".equals(yinString)) {
1412                             yinValue = true;
1413                             break outer;
1414                         }
1415                     }
1416                 }
1417             }
1418         }
1419         return yinValue;
1420     }
1421
1422     /**
1423      * Check this base type.
1424      *
1425      * @param typeName
1426      *            base YANG type name
1427      * @param moduleName
1428      *            name of current module
1429      * @param line
1430      *            line in module
1431      * @throws YangParseException
1432      *             if this is one of YANG type which MUST contain additional
1433      *             informations in its body
1434      */
1435     public static void checkMissingBody(final String typeName, final String moduleName, final int line) {
1436         switch (typeName) {
1437         case "decimal64":
1438             throw new YangParseException(moduleName, line,
1439                     "The 'fraction-digits' statement MUST be present if the type is 'decimal64'.");
1440         case "identityref":
1441             throw new YangParseException(moduleName, line,
1442                     "The 'base' statement MUST be present if the type is 'identityref'.");
1443         case "leafref":
1444             throw new YangParseException(moduleName, line,
1445                     "The 'path' statement MUST be present if the type is 'leafref'.");
1446         case "bits":
1447             throw new YangParseException(moduleName, line, "The 'bit' statement MUST be present if the type is 'bits'.");
1448         case "enumeration":
1449             throw new YangParseException(moduleName, line,
1450                     "The 'enum' statement MUST be present if the type is 'enumeration'.");
1451         }
1452     }
1453
1454     /**
1455      * Parse refine statement.
1456      *
1457      * @param refineCtx
1458      *            refine statement
1459      * @param moduleName
1460      *            name of current module
1461      * @return RefineHolder object representing this refine statement
1462      */
1463     public static RefineHolder parseRefine(Refine_stmtContext refineCtx, String moduleName) {
1464         final String refineTarget = stringFromNode(refineCtx);
1465         final RefineHolder refine = new RefineHolder(moduleName, refineCtx.getStart().getLine(), refineTarget);
1466         for (int i = 0; i < refineCtx.getChildCount(); i++) {
1467             ParseTree refinePom = refineCtx.getChild(i);
1468             if (refinePom instanceof Refine_pomContext) {
1469                 for (int j = 0; j < refinePom.getChildCount(); j++) {
1470                     ParseTree refineStmt = refinePom.getChild(j);
1471                     parseRefineDefault(refine, refineStmt);
1472
1473                     if (refineStmt instanceof Refine_leaf_stmtsContext) {
1474                         parseRefine(refine, (Refine_leaf_stmtsContext) refineStmt);
1475                     } else if (refineStmt instanceof Refine_container_stmtsContext) {
1476                         parseRefine(refine, (Refine_container_stmtsContext) refineStmt);
1477                     } else if (refineStmt instanceof Refine_list_stmtsContext) {
1478                         parseRefine(refine, (Refine_list_stmtsContext) refineStmt);
1479                     } else if (refineStmt instanceof Refine_leaf_list_stmtsContext) {
1480                         parseRefine(refine, (Refine_leaf_list_stmtsContext) refineStmt);
1481                     } else if (refineStmt instanceof Refine_choice_stmtsContext) {
1482                         parseRefine(refine, (Refine_choice_stmtsContext) refineStmt);
1483                     } else if (refineStmt instanceof Refine_anyxml_stmtsContext) {
1484                         parseRefine(refine, (Refine_anyxml_stmtsContext) refineStmt);
1485                     }
1486                 }
1487             }
1488         }
1489         return refine;
1490     }
1491
1492     private static void parseRefineDefault(RefineHolder refine, ParseTree refineStmt) {
1493         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1494             ParseTree refineArg = refineStmt.getChild(i);
1495             if (refineArg instanceof Description_stmtContext) {
1496                 String description = stringFromNode(refineArg);
1497                 refine.setDescription(description);
1498             } else if (refineArg instanceof Reference_stmtContext) {
1499                 String reference = stringFromNode(refineArg);
1500                 refine.setReference(reference);
1501             } else if (refineArg instanceof Config_stmtContext) {
1502                 Boolean config = parseConfig((Config_stmtContext) refineArg, refine.getModuleName());
1503                 refine.setConfiguration(config);
1504             }
1505         }
1506     }
1507
1508     private static RefineHolder parseRefine(RefineHolder refine, Refine_leaf_stmtsContext refineStmt) {
1509         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1510             ParseTree refineArg = refineStmt.getChild(i);
1511             if (refineArg instanceof Default_stmtContext) {
1512                 String defaultStr = stringFromNode(refineArg);
1513                 refine.setDefaultStr(defaultStr);
1514             } else if (refineArg instanceof Mandatory_stmtContext) {
1515                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1516                     ParseTree mandatoryTree = refineArg.getChild(j);
1517                     if (mandatoryTree instanceof Mandatory_argContext) {
1518                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1519                         refine.setMandatory(mandatory);
1520                     }
1521                 }
1522             } else if (refineArg instanceof Must_stmtContext) {
1523                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1524                 refine.setMust(must);
1525
1526             }
1527         }
1528         return refine;
1529     }
1530
1531     private static RefineHolder parseRefine(RefineHolder refine, Refine_container_stmtsContext refineStmt) {
1532         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1533             ParseTree refineArg = refineStmt.getChild(i);
1534             if (refineArg instanceof Must_stmtContext) {
1535                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1536                 refine.setMust(must);
1537             } else if (refineArg instanceof Presence_stmtContext) {
1538                 refine.setPresence(true);
1539             }
1540         }
1541         return refine;
1542     }
1543
1544     private static RefineHolder parseRefine(RefineHolder refine, Refine_list_stmtsContext refineStmt) {
1545         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1546             ParseTree refineArg = refineStmt.getChild(i);
1547             if (refineArg instanceof Must_stmtContext) {
1548                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1549                 refine.setMust(must);
1550             } else if (refineArg instanceof Max_elements_stmtContext) {
1551                 Integer max = parseMaxElements((Max_elements_stmtContext) refineArg, refine.getModuleName());
1552                 refine.setMaxElements(max);
1553             } else if (refineArg instanceof Min_elements_stmtContext) {
1554                 Integer min = parseMinElements((Min_elements_stmtContext) refineArg, refine.getModuleName());
1555                 refine.setMinElements(min);
1556             }
1557         }
1558         return refine;
1559     }
1560
1561     private static RefineHolder parseRefine(RefineHolder refine, Refine_leaf_list_stmtsContext refineStmt) {
1562         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1563             ParseTree refineArg = refineStmt.getChild(i);
1564             if (refineArg instanceof Must_stmtContext) {
1565                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1566                 refine.setMust(must);
1567             } else if (refineArg instanceof Max_elements_stmtContext) {
1568                 Integer max = parseMaxElements((Max_elements_stmtContext) refineArg, refine.getModuleName());
1569                 refine.setMaxElements(max);
1570             } else if (refineArg instanceof Min_elements_stmtContext) {
1571                 Integer min = parseMinElements((Min_elements_stmtContext) refineArg, refine.getModuleName());
1572                 refine.setMinElements(min);
1573             }
1574         }
1575         return refine;
1576     }
1577
1578     private static RefineHolder parseRefine(RefineHolder refine, Refine_choice_stmtsContext refineStmt) {
1579         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1580             ParseTree refineArg = refineStmt.getChild(i);
1581             if (refineArg instanceof Default_stmtContext) {
1582                 String defaultStr = stringFromNode(refineArg);
1583                 refine.setDefaultStr(defaultStr);
1584             } else if (refineArg instanceof Mandatory_stmtContext) {
1585                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1586                     ParseTree mandatoryTree = refineArg.getChild(j);
1587                     if (mandatoryTree instanceof Mandatory_argContext) {
1588                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1589                         refine.setMandatory(mandatory);
1590                     }
1591                 }
1592             }
1593         }
1594         return refine;
1595     }
1596
1597     private static RefineHolder parseRefine(RefineHolder refine, Refine_anyxml_stmtsContext refineStmt) {
1598         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1599             ParseTree refineArg = refineStmt.getChild(i);
1600             if (refineArg instanceof Must_stmtContext) {
1601                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1602                 refine.setMust(must);
1603             } else if (refineArg instanceof Mandatory_stmtContext) {
1604                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1605                     ParseTree mandatoryTree = refineArg.getChild(j);
1606                     if (mandatoryTree instanceof Mandatory_argContext) {
1607                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1608                         refine.setMandatory(mandatory);
1609                     }
1610                 }
1611             }
1612         }
1613         return refine;
1614     }
1615
1616 }