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