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