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