Merge from development repository.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / yang-model-parser-impl / src / main / java / org / opendaylight / controller / yang / model / parser / util / YangModelBuilderUtil.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.model.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.YangParser;
19 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Bit_stmtContext;
20 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Bits_specificationContext;
21 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Config_argContext;
22 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Config_stmtContext;
23 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Decimal64_specificationContext;
24 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Description_stmtContext;
25 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Enum_specificationContext;
26 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Enum_stmtContext;
27 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Fraction_digits_stmtContext;
28 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leafref_specificationContext;
29 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Length_stmtContext;
30 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_argContext;
31 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_stmtContext;
32 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Max_elements_stmtContext;
33 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Min_elements_stmtContext;
34 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Must_stmtContext;
35 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Numerical_restrictionsContext;
36 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Ordered_by_argContext;
37 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Ordered_by_stmtContext;
38 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Path_stmtContext;
39 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Pattern_stmtContext;
40 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Position_stmtContext;
41 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Range_stmtContext;
42 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Reference_stmtContext;
43 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Require_instance_argContext;
44 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Require_instance_stmtContext;
45 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Status_argContext;
46 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Status_stmtContext;
47 import org.opendaylight.controller.antlrv4.code.gen.YangParser.StringContext;
48 import org.opendaylight.controller.antlrv4.code.gen.YangParser.String_restrictionsContext;
49 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Type_body_stmtsContext;
50 import org.opendaylight.controller.antlrv4.code.gen.YangParser.Units_stmtContext;
51 import org.opendaylight.controller.antlrv4.code.gen.YangParser.When_stmtContext;
52 import org.opendaylight.controller.model.api.type.BitsTypeDefinition;
53 import org.opendaylight.controller.model.api.type.BitsTypeDefinition.Bit;
54 import org.opendaylight.controller.model.api.type.EnumTypeDefinition;
55 import org.opendaylight.controller.model.api.type.LengthConstraint;
56 import org.opendaylight.controller.model.api.type.PatternConstraint;
57 import org.opendaylight.controller.model.api.type.RangeConstraint;
58 import org.opendaylight.controller.model.util.BaseConstraints;
59 import org.opendaylight.controller.model.util.BinaryType;
60 import org.opendaylight.controller.model.util.BitsType;
61 import org.opendaylight.controller.model.util.EnumerationType;
62 import org.opendaylight.controller.model.util.InstanceIdentifier;
63 import org.opendaylight.controller.model.util.Leafref;
64 import org.opendaylight.controller.model.util.RevisionAwareXPathImpl;
65 import org.opendaylight.controller.model.util.StringType;
66 import org.opendaylight.controller.model.util.UnknownType;
67 import org.opendaylight.controller.model.util.YangTypesConverter;
68 import org.opendaylight.controller.yang.common.QName;
69 import org.opendaylight.controller.yang.model.api.RevisionAwareXPath;
70 import org.opendaylight.controller.yang.model.api.SchemaPath;
71 import org.opendaylight.controller.yang.model.api.Status;
72 import org.opendaylight.controller.yang.model.api.TypeDefinition;
73 import org.opendaylight.controller.yang.model.api.UnknownSchemaNode;
74 import org.opendaylight.controller.yang.model.parser.builder.api.SchemaNodeBuilder;
75 import org.opendaylight.controller.yang.model.parser.builder.impl.ConstraintsBuilder;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 public class YangModelBuilderUtil {
80
81     private static final Logger logger = LoggerFactory
82             .getLogger(YangModelBuilderUtil.class);
83
84     /**
85      * Parse given tree and get first string value.
86      *
87      * @param treeNode
88      *            tree to parse
89      * @return first string value from given tree
90      */
91     public static String stringFromNode(final ParseTree treeNode) {
92         final String result = "";
93         for (int i = 0; i < treeNode.getChildCount(); ++i) {
94             if (treeNode.getChild(i) instanceof StringContext) {
95                 final StringContext context = (StringContext) treeNode
96                         .getChild(i);
97                 if (context != null) {
98                     return context.getChild(0).getText().replace("\"", "");
99                 }
100             }
101         }
102         return result;
103     }
104
105     /**
106      * Parse 'description', 'reference' and 'status' statements and fill in
107      * given builder.
108      *
109      * @param ctx
110      *            context to parse
111      * @param builder
112      *            builder to fill in with parsed statements
113      */
114     public static void parseSchemaNodeArgs(ParseTree ctx,
115             SchemaNodeBuilder builder) {
116         for (int i = 0; i < ctx.getChildCount(); i++) {
117             ParseTree child = ctx.getChild(i);
118             if (child instanceof Description_stmtContext) {
119                 String desc = stringFromNode(child);
120                 builder.setDescription(desc);
121             } else if (child instanceof Reference_stmtContext) {
122                 String ref = stringFromNode(child);
123                 builder.setReference(ref);
124             } else if (child instanceof Status_stmtContext) {
125                 Status status = parseStatus((Status_stmtContext) child);
126                 builder.setStatus(status);
127             }
128         }
129     }
130
131     /**
132      * Parse given context and return its value;
133      *
134      * @param ctx
135      *            status context
136      * @return value parsed from context
137      */
138     public static Status parseStatus(Status_stmtContext ctx) {
139         for (int i = 0; i < ctx.getChildCount(); i++) {
140             ParseTree statusArg = ctx.getChild(i);
141             if (statusArg instanceof Status_argContext) {
142                 String statusArgStr = stringFromNode(statusArg);
143                 if (statusArgStr.equals("current")) {
144                     return Status.CURRENT;
145                 } else if (statusArgStr.equals("deprecated")) {
146                     return Status.DEPRECATED;
147                 } else if (statusArgStr.equals("obsolete")) {
148                     return Status.OBSOLETE;
149                 } else {
150                     logger.warn("Invalid 'status' statement: " + statusArgStr);
151                 }
152             }
153         }
154         return null;
155     }
156
157     /**
158      * Parse given tree and returns units statement as string.
159      *
160      * @param ctx
161      *            context to parse
162      * @return value of units statement as string or null if there is no units
163      *         statement
164      */
165     public static String parseUnits(ParseTree ctx) {
166         String units = null;
167         for (int i = 0; i < ctx.getChildCount(); i++) {
168             ParseTree child = ctx.getChild(i);
169             if (child instanceof Units_stmtContext) {
170                 units = stringFromNode(child);
171                 break;
172             }
173         }
174         return units;
175     }
176
177     /**
178      * Create SchemaPath object from given path list with namespace, revision
179      * and prefix based on given values.
180      *
181      * @param actualPath
182      * @param namespace
183      * @param revision
184      * @param prefix
185      * @return SchemaPath object.
186      */
187     public static SchemaPath createActualSchemaPath(List<String> actualPath,
188             URI namespace, Date revision, String prefix) {
189         final List<QName> path = new ArrayList<QName>();
190         QName qname;
191         for (String pathElement : actualPath) {
192             qname = new QName(namespace, revision, prefix, pathElement);
193             path.add(qname);
194         }
195         return new SchemaPath(path, true);
196     }
197
198     /**
199      * Create SchemaPath from given string.
200      *
201      * @param augmentPath
202      *            string representation of path
203      * @return SchemaPath object
204      */
205     public static SchemaPath parseAugmentPath(String augmentPath) {
206         boolean absolute = augmentPath.startsWith("/");
207         String[] splittedPath = augmentPath.split("/");
208         List<QName> path = new ArrayList<QName>();
209         QName name;
210         for (String pathElement : splittedPath) {
211             if (pathElement.length() > 0) {
212                 String[] splittedElement = pathElement.split(":");
213                 if (splittedElement.length == 1) {
214                     name = new QName(null, null, null, splittedElement[0]);
215                 } else {
216                     name = new QName(null, null, splittedElement[0],
217                             splittedElement[1]);
218                 }
219                 path.add(name);
220             }
221         }
222         return new SchemaPath(path, absolute);
223     }
224
225     /**
226      * Create java.util.List of QName objects from given key definition as
227      * string.
228      *
229      * @param keyDefinition
230      *            key definition as string
231      * @param namespace
232      *            current namespace
233      * @param revision
234      *            current revision
235      * @param prefix
236      *            current prefix
237      * @return YANG list key as java.util.List of QName objects
238      */
239     public static List<QName> createListKey(String keyDefinition,
240             URI namespace, Date revision, String prefix) {
241         List<QName> key = new ArrayList<QName>();
242         String[] splittedKey = keyDefinition.split(" ");
243
244         QName qname = null;
245         for (String keyElement : splittedKey) {
246             if (keyElement.length() != 0) {
247                 qname = new QName(namespace, revision, prefix, keyElement);
248                 key.add(qname);
249             }
250         }
251         return key;
252     }
253
254     private static List<EnumTypeDefinition.EnumPair> getEnumConstants(
255             Type_body_stmtsContext ctx, List<String> path, URI namespace,
256             Date revision, String prefix) {
257         List<EnumTypeDefinition.EnumPair> enumConstants = new ArrayList<EnumTypeDefinition.EnumPair>();
258
259         out: for (int j = 0; j < ctx.getChildCount(); j++) {
260             ParseTree enumSpecChild = ctx.getChild(j);
261             if (enumSpecChild instanceof Enum_specificationContext) {
262                 for (int k = 0; k < enumSpecChild.getChildCount(); k++) {
263                     ParseTree enumChild = enumSpecChild.getChild(k);
264                     if (enumChild instanceof Enum_stmtContext) {
265                         enumConstants.add(createEnumPair(
266                                 (Enum_stmtContext) enumChild, k, path,
267                                 namespace, revision, prefix));
268                         if (k == enumSpecChild.getChildCount() - 1) {
269                             break out;
270                         }
271                     }
272                 }
273             }
274         }
275         return enumConstants;
276     }
277
278     private static EnumTypeDefinition.EnumPair createEnumPair(
279             Enum_stmtContext ctx, final int value, List<String> path,
280             final URI namespace, final Date revision, final String prefix) {
281         final String name = stringFromNode(ctx);
282         final QName qname = new QName(namespace, revision, prefix, name);
283         String description = null;
284         String reference = null;
285         Status status = null;
286         List<String> enumPairPath = new ArrayList<String>(path);
287         enumPairPath.add(name);
288
289         for (int i = 0; i < ctx.getChildCount(); i++) {
290             ParseTree child = ctx.getChild(i);
291             if (child instanceof Description_stmtContext) {
292                 description = stringFromNode(child);
293             } else if (child instanceof Reference_stmtContext) {
294                 reference = stringFromNode(child);
295             } else if (child instanceof Status_stmtContext) {
296                 status = parseStatus((Status_stmtContext) child);
297             }
298         }
299
300         EnumPairImpl result = new EnumPairImpl();
301         result.qname = qname;
302         result.path = createActualSchemaPath(enumPairPath, namespace, revision,
303                 prefix);
304         result.description = description;
305         result.reference = reference;
306         result.status = status;
307         // TODO: extensionSchemaNodes
308         result.name = name;
309         result.value = value;
310         return result;
311     }
312
313     private static class EnumPairImpl implements EnumTypeDefinition.EnumPair {
314
315         private QName qname;
316         private SchemaPath path;
317         private String description;
318         private String reference;
319         private Status status;
320         private List<UnknownSchemaNode> extensionSchemaNodes = Collections
321                 .emptyList();
322         private String name;
323         private Integer value;
324
325         @Override
326         public QName getQName() {
327             return qname;
328         }
329
330         @Override
331         public SchemaPath getPath() {
332             return path;
333         }
334
335         @Override
336         public String getDescription() {
337             return description;
338         }
339
340         @Override
341         public String getReference() {
342             return reference;
343         }
344
345         @Override
346         public Status getStatus() {
347             return status;
348         }
349
350         @Override
351         public List<UnknownSchemaNode> getUnknownSchemaNodes() {
352             return extensionSchemaNodes;
353         }
354
355         @Override
356         public String getName() {
357             return name;
358         }
359
360         @Override
361         public Integer getValue() {
362             return value;
363         }
364
365         @Override
366         public int hashCode() {
367             final int prime = 31;
368             int result = 1;
369             result = prime * result + ((qname == null) ? 0 : qname.hashCode());
370             result = prime * result + ((path == null) ? 0 : path.hashCode());
371             result = prime * result
372                     + ((description == null) ? 0 : description.hashCode());
373             result = prime * result
374                     + ((reference == null) ? 0 : reference.hashCode());
375             result = prime * result
376                     + ((status == null) ? 0 : status.hashCode());
377             result = prime
378                     * result
379                     + ((extensionSchemaNodes == null) ? 0
380                             : extensionSchemaNodes.hashCode());
381             result = prime * result + ((name == null) ? 0 : name.hashCode());
382             result = prime * result + ((value == null) ? 0 : value.hashCode());
383             return result;
384         }
385
386         @Override
387         public boolean equals(Object obj) {
388             if (this == obj) {
389                 return true;
390             }
391             if (obj == null) {
392                 return false;
393             }
394             if (getClass() != obj.getClass()) {
395                 return false;
396             }
397             EnumPairImpl other = (EnumPairImpl) obj;
398             if (qname == null) {
399                 if (other.qname != null) {
400                     return false;
401                 }
402             } else if (!qname.equals(other.qname)) {
403                 return false;
404             }
405             if (path == null) {
406                 if (other.path != null) {
407                     return false;
408                 }
409             } else if (!path.equals(other.path)) {
410                 return false;
411             }
412             if (description == null) {
413                 if (other.description != null) {
414                     return false;
415                 }
416             } else if (!description.equals(other.description)) {
417                 return false;
418             }
419             if (reference == null) {
420                 if (other.reference != null) {
421                     return false;
422                 }
423             } else if (!reference.equals(other.reference)) {
424                 return false;
425             }
426             if (status == null) {
427                 if (other.status != null) {
428                     return false;
429                 }
430             } else if (!status.equals(other.status)) {
431                 return false;
432             }
433             if (extensionSchemaNodes == null) {
434                 if (other.extensionSchemaNodes != null) {
435                     return false;
436                 }
437             } else if (!extensionSchemaNodes.equals(other.extensionSchemaNodes)) {
438                 return false;
439             }
440             if (name == null) {
441                 if (other.name != null) {
442                     return false;
443                 }
444             } else if (!name.equals(other.name)) {
445                 return false;
446             }
447             if (value == null) {
448                 if (other.value != null) {
449                     return false;
450                 }
451             } else if (!value.equals(other.value)) {
452                 return false;
453             }
454             return true;
455         }
456
457         @Override
458         public String toString() {
459             return EnumTypeDefinition.EnumPair.class.getSimpleName() + "[name="
460                     + name + ", value=" + value + "]";
461         }
462     };
463
464     private static List<RangeConstraint> getRangeConstraints(
465             Type_body_stmtsContext ctx) {
466         final List<RangeConstraint> rangeConstraints = new ArrayList<RangeConstraint>();
467         for (int j = 0; j < ctx.getChildCount(); j++) {
468             ParseTree numRestrChild = ctx.getChild(j);
469             if (numRestrChild instanceof Numerical_restrictionsContext) {
470                 for (int k = 0; k < numRestrChild.getChildCount(); k++) {
471                     ParseTree rangeChild = numRestrChild.getChild(k);
472                     if (rangeChild instanceof Range_stmtContext) {
473                         rangeConstraints
474                                 .addAll(parseRangeConstraints((Range_stmtContext) rangeChild));
475                         break;
476                     }
477                 }
478             }
479         }
480         return rangeConstraints;
481     }
482
483     private static List<RangeConstraint> parseRangeConstraints(
484             Range_stmtContext ctx) {
485         List<RangeConstraint> rangeConstraints = new ArrayList<RangeConstraint>();
486         String description = null;
487         String reference = null;
488
489         for (int i = 0; i < ctx.getChildCount(); i++) {
490             ParseTree child = ctx.getChild(i);
491             if (child instanceof Description_stmtContext) {
492                 description = stringFromNode(child);
493             } else if (child instanceof Reference_stmtContext) {
494                 reference = stringFromNode(child);
495             }
496         }
497
498         String rangeStr = stringFromNode(ctx);
499         String trimmed = rangeStr.replace(" ", "");
500         String[] splittedRange = trimmed.split("\\|");
501         for (String rangeDef : splittedRange) {
502             String[] splittedRangeDef = rangeDef.split("\\.\\.");
503             Long min;
504             Long max;
505             if (splittedRangeDef.length == 1) {
506                 min = max = parseRangeValue(splittedRangeDef[0]);
507             } else {
508                 min = parseRangeValue(splittedRangeDef[0]);
509                 max = parseRangeValue(splittedRangeDef[1]);
510             }
511             RangeConstraint range = BaseConstraints.rangeConstraint(min, max,
512                     description, reference);
513             rangeConstraints.add(range);
514         }
515
516         return rangeConstraints;
517     }
518
519     private static List<LengthConstraint> getLengthConstraints(
520             Type_body_stmtsContext ctx) {
521         List<LengthConstraint> lengthConstraints = new ArrayList<LengthConstraint>();
522         for (int j = 0; j < ctx.getChildCount(); j++) {
523             ParseTree stringRestrChild = ctx.getChild(j);
524             if (stringRestrChild instanceof String_restrictionsContext) {
525                 for (int k = 0; k < stringRestrChild.getChildCount(); k++) {
526                     ParseTree lengthChild = stringRestrChild.getChild(k);
527                     if (lengthChild instanceof Length_stmtContext) {
528                         lengthConstraints
529                                 .addAll(parseLengthConstraints((Length_stmtContext) lengthChild));
530                     }
531                 }
532             }
533         }
534         return lengthConstraints;
535     }
536
537     private static List<LengthConstraint> parseLengthConstraints(
538             Length_stmtContext ctx) {
539         List<LengthConstraint> lengthConstraints = new ArrayList<LengthConstraint>();
540         String description = null;
541         String reference = null;
542
543         for (int i = 0; i < ctx.getChildCount(); i++) {
544             ParseTree child = ctx.getChild(i);
545             if (child instanceof Description_stmtContext) {
546                 description = stringFromNode(child);
547             } else if (child instanceof Reference_stmtContext) {
548                 reference = stringFromNode(child);
549             }
550         }
551
552         String lengthStr = stringFromNode(ctx);
553         String trimmed = lengthStr.replace(" ", "");
554         String[] splittedRange = trimmed.split("\\|");
555         for (String rangeDef : splittedRange) {
556             String[] splittedRangeDef = rangeDef.split("\\.\\.");
557             Long min;
558             Long max;
559             if (splittedRangeDef.length == 1) {
560                 min = max = parseRangeValue(splittedRangeDef[0]);
561             } else {
562                 min = parseRangeValue(splittedRangeDef[0]);
563                 max = parseRangeValue(splittedRangeDef[1]);
564             }
565             LengthConstraint range = BaseConstraints.lengthConstraint(min, max,
566                     description, reference);
567             lengthConstraints.add(range);
568         }
569
570         return lengthConstraints;
571     }
572
573     private static Long parseRangeValue(String value) {
574         Long result = null;
575         if (value.equals("min")) {
576             result = Long.MIN_VALUE;
577         } else if (value.equals("max")) {
578             result = Long.MAX_VALUE;
579         } else {
580             result = Long.valueOf(value);
581         }
582         return result;
583     }
584
585     private static List<PatternConstraint> getPatternConstraint(
586             Type_body_stmtsContext ctx) {
587         List<PatternConstraint> patterns = new ArrayList<PatternConstraint>();
588
589         out: for (int j = 0; j < ctx.getChildCount(); j++) {
590             ParseTree stringRestrChild = ctx.getChild(j);
591             if (stringRestrChild instanceof String_restrictionsContext) {
592                 for (int k = 0; k < stringRestrChild.getChildCount(); k++) {
593                     ParseTree lengthChild = stringRestrChild.getChild(k);
594                     if (lengthChild instanceof Pattern_stmtContext) {
595                         patterns.add(parsePatternConstraint((Pattern_stmtContext) lengthChild));
596                         if (k == lengthChild.getChildCount() - 1) {
597                             break out;
598                         }
599                     }
600                 }
601             }
602         }
603         return patterns;
604     }
605
606     /**
607      * Internal helper method.
608      *
609      * @param ctx
610      *            pattern context
611      * @return PatternConstraint object
612      */
613     private static PatternConstraint parsePatternConstraint(
614             Pattern_stmtContext ctx) {
615         String description = null;
616         String reference = null;
617         for (int i = 0; i < ctx.getChildCount(); i++) {
618             ParseTree child = ctx.getChild(i);
619             if (child instanceof Description_stmtContext) {
620                 description = stringFromNode(child);
621             } else if (child instanceof Reference_stmtContext) {
622                 reference = stringFromNode(child);
623             }
624         }
625         String pattern = stringFromNode(ctx);
626         return BaseConstraints.patternConstraint(pattern, description,
627                 reference);
628     }
629
630     private static Integer getFractionDigits(Type_body_stmtsContext ctx) {
631         for (int j = 0; j < ctx.getChildCount(); j++) {
632             ParseTree dec64specChild = ctx.getChild(j);
633             if (dec64specChild instanceof Decimal64_specificationContext) {
634                 return parseFractionDigits((Decimal64_specificationContext) dec64specChild);
635             }
636         }
637         return null;
638     }
639
640     private static Integer parseFractionDigits(
641             Decimal64_specificationContext ctx) {
642         for (int k = 0; k < ctx.getChildCount(); k++) {
643             ParseTree fdChild = ctx.getChild(k);
644             if (fdChild instanceof Fraction_digits_stmtContext) {
645                 return Integer.valueOf(stringFromNode(fdChild));
646             }
647         }
648         return null;
649     }
650
651     private static List<BitsTypeDefinition.Bit> getBits(
652             Type_body_stmtsContext ctx, List<String> actualPath, URI namespace,
653             Date revision, String prefix) {
654         List<BitsTypeDefinition.Bit> bits = new ArrayList<BitsTypeDefinition.Bit>();
655         for (int j = 0; j < ctx.getChildCount(); j++) {
656             ParseTree bitsSpecChild = ctx.getChild(j);
657             if (bitsSpecChild instanceof Bits_specificationContext) {
658                 for (int k = 0; k < bitsSpecChild.getChildCount(); k++) {
659                     ParseTree bitChild = bitsSpecChild.getChild(k);
660                     if (bitChild instanceof Bit_stmtContext) {
661                         bits.add(parseBit((Bit_stmtContext) bitChild,
662                                 actualPath, namespace, revision, prefix));
663                     }
664                 }
665             }
666         }
667         return bits;
668     }
669
670     private static boolean isRequireInstance(Type_body_stmtsContext ctx) {
671         for (int i = 0; i < ctx.getChildCount(); i++) {
672             ParseTree child = ctx.getChild(i);
673             if (child instanceof Require_instance_stmtContext) {
674                 for (int j = 0; j < child.getChildCount(); j++) {
675                     ParseTree reqArg = child.getChild(j);
676                     if (reqArg instanceof Require_instance_argContext) {
677                         return Boolean.valueOf(stringFromNode(reqArg));
678                     }
679                 }
680             }
681         }
682         return false;
683     }
684
685     private static BitsTypeDefinition.Bit parseBit(final Bit_stmtContext ctx,
686             List<String> actualPath, final URI namespace, final Date revision,
687             final String prefix) {
688         String name = stringFromNode(ctx);
689         final QName qname = new QName(namespace, revision, prefix, name);
690         Long position = null;
691
692         String description = null;
693         String reference = null;
694         Status status = Status.CURRENT;
695
696         Stack<String> bitPath = new Stack<String>();
697         bitPath.addAll(actualPath);
698         bitPath.add(name);
699
700         SchemaPath schemaPath = createActualSchemaPath(bitPath, namespace,
701                 revision, prefix);
702
703         for (int i = 0; i < ctx.getChildCount(); i++) {
704             ParseTree child = ctx.getChild(i);
705             if (child instanceof Position_stmtContext) {
706                 String positionStr = stringFromNode(child);
707                 position = Long.valueOf(positionStr);
708                 if (position < 0 || position > 4294967295L) {
709                     throw new IllegalArgumentException(
710                             "position value MUST be in the range 0 to 4294967295, but was: "
711                                     + position);
712                 }
713             } else if (child instanceof Description_stmtContext) {
714                 description = stringFromNode(child);
715             } else if (child instanceof Reference_stmtContext) {
716                 reference = stringFromNode(child);
717             } else if (child instanceof Status_stmtContext) {
718                 status = parseStatus((Status_stmtContext) child);
719             }
720         }
721
722         // TODO: extensionDefinitions
723         return createBit(qname, schemaPath, description, reference, status,
724                 null, position);
725     }
726
727     private static BitsTypeDefinition.Bit createBit(final QName qname,
728             final SchemaPath schemaPath, final String description,
729             final String reference, final Status status,
730             final List<UnknownSchemaNode> extensionDefinitions,
731             final Long position) {
732         return new BitsTypeDefinition.Bit() {
733
734             @Override
735             public QName getQName() {
736                 return qname;
737             }
738
739             @Override
740             public SchemaPath getPath() {
741                 return schemaPath;
742             }
743
744             @Override
745             public String getDescription() {
746                 return description;
747             }
748
749             @Override
750             public String getReference() {
751                 return reference;
752             }
753
754             @Override
755             public Status getStatus() {
756                 return status;
757             }
758
759             @Override
760             public List<UnknownSchemaNode> getUnknownSchemaNodes() {
761                 return extensionDefinitions;
762             }
763
764             @Override
765             public Long getPosition() {
766                 return position;
767             }
768
769             @Override
770             public String getName() {
771                 return qname.getLocalName();
772             }
773
774             @Override
775             public int hashCode() {
776                 final int prime = 31;
777                 int result = 1;
778                 result = prime * result
779                         + ((qname == null) ? 0 : qname.hashCode());
780                 result = prime * result
781                         + ((schemaPath == null) ? 0 : schemaPath.hashCode());
782                 result = prime * result
783                         + ((description == null) ? 0 : description.hashCode());
784                 result = prime * result
785                         + ((reference == null) ? 0 : reference.hashCode());
786                 result = prime * result
787                         + ((status == null) ? 0 : status.hashCode());
788                 result = prime * result
789                         + ((position == null) ? 0 : position.hashCode());
790                 result = prime
791                         * result
792                         + ((extensionDefinitions == null) ? 0
793                                 : extensionDefinitions.hashCode());
794                 return result;
795             }
796
797             @Override
798             public boolean equals(Object obj) {
799                 if (this == obj) {
800                     return true;
801                 }
802                 if (obj == null) {
803                     return false;
804                 }
805                 if (getClass() != obj.getClass()) {
806                     return false;
807                 }
808                 Bit other = (Bit) obj;
809                 if (qname == null) {
810                     if (other.getQName() != null) {
811                         return false;
812                     }
813                 } else if (!qname.equals(other.getQName())) {
814                     return false;
815                 }
816                 if (schemaPath == null) {
817                     if (other.getPath() != null) {
818                         return false;
819                     }
820                 } else if (!schemaPath.equals(other.getPath())) {
821                     return false;
822                 }
823                 if (description == null) {
824                     if (other.getDescription() != null) {
825                         return false;
826                     }
827                 } else if (!description.equals(other.getDescription())) {
828                     return false;
829                 }
830                 if (reference == null) {
831                     if (other.getReference() != null) {
832                         return false;
833                     }
834                 } else if (!reference.equals(other.getReference())) {
835                     return false;
836                 }
837                 if (status == null) {
838                     if (other.getStatus() != null) {
839                         return false;
840                     }
841                 } else if (!status.equals(other.getStatus())) {
842                     return false;
843                 }
844                 if (extensionDefinitions == null) {
845                     if (other.getUnknownSchemaNodes() != null) {
846                         return false;
847                     }
848                 } else if (!extensionDefinitions.equals(other
849                         .getUnknownSchemaNodes())) {
850                     return false;
851                 }
852                 if (position == null) {
853                     if (other.getPosition() != null) {
854                         return false;
855                     }
856                 } else if (!position.equals(other.getPosition())) {
857                     return false;
858                 }
859                 return true;
860             }
861
862             @Override
863             public String toString() {
864                 return Bit.class.getSimpleName() + "[name="
865                         + qname.getLocalName() + ", position=" + position + "]";
866             }
867         };
868     }
869
870     /**
871      * Parse orderedby statement.
872      *
873      * @param childNode
874      *            Ordered_by_stmtContext
875      * @return true, if orderedby contains value 'user' or false otherwise
876      */
877     public static boolean parseUserOrdered(Ordered_by_stmtContext childNode) {
878         boolean result = false;
879         for (int j = 0; j < childNode.getChildCount(); j++) {
880             ParseTree orderArg = childNode.getChild(j);
881             if (orderArg instanceof Ordered_by_argContext) {
882                 String orderStr = stringFromNode(orderArg);
883                 if (orderStr.equals("system")) {
884                     result = false;
885                 } else if (orderStr.equals("user")) {
886                     result = true;
887                 } else {
888                     logger.warn("Invalid 'orderedby' statement.");
889                 }
890             }
891         }
892         return result;
893     }
894
895     /**
896      * Parse given config context and return true if it contains string 'true',
897      * false otherwise.
898      *
899      * @param ctx
900      *            config context to parse.
901      * @return true if given context contains string 'true', false otherwise
902      */
903     public static boolean parseConfig(final Config_stmtContext ctx) {
904         if (ctx != null) {
905             for (int i = 0; i < ctx.getChildCount(); ++i) {
906                 final ParseTree configContext = ctx.getChild(i);
907                 if (configContext instanceof Config_argContext) {
908                     final String value = stringFromNode(configContext);
909                     if (value.equals("true")) {
910                         return true;
911                     }
912                 }
913             }
914         }
915         return false;
916     }
917
918     /**
919      * Parse given type body and creates UnknownType definition.
920      *
921      * @param typedefQName
922      *            qname of current type
923      * @param ctx
924      *            type body
925      * @return UnknownType object with constraints from parsed type body
926      */
927     public static TypeDefinition<?> parseUnknownTypeBody(QName typedefQName,
928             Type_body_stmtsContext ctx) {
929         UnknownType.Builder ut = new UnknownType.Builder(typedefQName);
930
931         if (ctx != null) {
932             List<RangeConstraint> rangeStatements = getRangeConstraints(ctx);
933             List<LengthConstraint> lengthStatements = getLengthConstraints(ctx);
934             List<PatternConstraint> patternStatements = getPatternConstraint(ctx);
935
936             ut.rangeStatements(rangeStatements);
937             ut.lengthStatements(lengthStatements);
938             ut.patterns(patternStatements);
939         }
940
941         return ut.build();
942     }
943
944     /**
945      * Create TypeDefinition object based on given type name and type body.
946      *
947      * @param typeName
948      *            name of type
949      * @param typeBody
950      *            type body
951      * @param actualPath
952      *            current path in schema
953      * @param namespace
954      *            current namespace
955      * @param revision
956      *            current revision
957      * @param prefix
958      *            current prefix
959      * @return TypeDefinition object based on parsed values.
960      */
961     public static TypeDefinition<?> parseTypeBody(String typeName,
962             Type_body_stmtsContext typeBody, List<String> actualPath,
963             URI namespace, Date revision, String prefix) {
964         TypeDefinition<?> type = null;
965
966         List<RangeConstraint> rangeStatements = getRangeConstraints(typeBody);
967         Integer fractionDigits = getFractionDigits(typeBody);
968         List<LengthConstraint> lengthStatements = getLengthConstraints(typeBody);
969         List<PatternConstraint> patternStatements = getPatternConstraint(typeBody);
970         List<EnumTypeDefinition.EnumPair> enumConstants = getEnumConstants(typeBody, actualPath, namespace, revision, prefix);
971
972         if (typeName.equals("decimal64")) {
973             type = YangTypesConverter.javaTypeForBaseYangDecimal64Type(
974                     rangeStatements, fractionDigits);
975         } else if (typeName.startsWith("int") || typeName.startsWith("uint")) {
976             type = YangTypesConverter.javaTypeForBaseYangIntegerType(typeName,
977                     rangeStatements);
978         } else if (typeName.equals("enumeration")) {
979             type = new EnumerationType(enumConstants);
980         } else if (typeName.equals("string")) {
981             type = new StringType(lengthStatements, patternStatements);
982         } else if (typeName.equals("bits")) {
983             type = new BitsType(getBits(typeBody, actualPath, namespace,
984                     revision, prefix));
985         } else if (typeName.equals("leafref")) {
986             final String path = parseLeafrefPath(typeBody);
987             final boolean absolute = path.startsWith("/");
988             RevisionAwareXPath xpath = new RevisionAwareXPathImpl(path,
989                     absolute);
990             type = new Leafref(xpath);
991         } else if (typeName.equals("binary")) {
992             type = new BinaryType(null, lengthStatements, null);
993         } else if (typeName.equals("instanceidentifier")) {
994             boolean requireInstance = isRequireInstance(typeBody);
995             type = new InstanceIdentifier(null, requireInstance);
996         }
997         return type;
998     }
999
1000     private static String parseLeafrefPath(Type_body_stmtsContext ctx) {
1001         for (int i = 0; i < ctx.getChildCount(); i++) {
1002             ParseTree child = ctx.getChild(i);
1003             if (child instanceof Leafref_specificationContext) {
1004                 for (int j = 0; j < child.getChildCount(); j++) {
1005                     ParseTree leafRefSpec = child.getChild(j);
1006                     if (leafRefSpec instanceof Path_stmtContext) {
1007                         return stringFromNode(leafRefSpec);
1008                     }
1009                 }
1010             }
1011         }
1012         return null;
1013     }
1014
1015     /**
1016      * Internal helper method for parsing Must_stmtContext.
1017      *
1018      * @param ctx
1019      *            Must_stmtContext
1020      * @return an array of strings with following fields: [0] must text [1]
1021      *         description [2] reference
1022      */
1023     public static String[] parseMust(YangParser.Must_stmtContext ctx) {
1024         String[] params = new String[3];
1025
1026         String mustText = "";
1027         String description = null;
1028         String reference = null;
1029         for (int i = 0; i < ctx.getChildCount(); ++i) {
1030             ParseTree child = ctx.getChild(i);
1031             if (child instanceof StringContext) {
1032                 final StringContext context = (StringContext) child;
1033                 for (int j = 0; j < context.getChildCount(); j++) {
1034                     String mustPart = context.getChild(j).getText();
1035                     if (j == 0) {
1036                         mustText += mustPart
1037                                 .substring(0, mustPart.length() - 1);
1038                         continue;
1039                     }
1040                     if (j % 2 == 0) {
1041                         mustText += mustPart.substring(1);
1042                     }
1043                 }
1044             } else if (child instanceof Description_stmtContext) {
1045                 description = stringFromNode(child);
1046             } else if (child instanceof Reference_stmtContext) {
1047                 reference = stringFromNode(child);
1048             }
1049         }
1050         params[0] = mustText;
1051         params[1] = description;
1052         params[2] = reference;
1053
1054         return params;
1055     }
1056
1057     /**
1058      * Parse given tree and set constraints to given builder.
1059      *
1060      * @param ctx
1061      *            Context to search.
1062      * @param constraintsBuilder
1063      *            ConstraintsBuilder to fill.
1064      */
1065     public static void parseConstraints(ParseTree ctx,
1066             ConstraintsBuilder constraintsBuilder) {
1067         for (int i = 0; i < ctx.getChildCount(); ++i) {
1068             final ParseTree childNode = ctx.getChild(i);
1069             if (childNode instanceof Max_elements_stmtContext) {
1070                 Integer max = Integer.valueOf(stringFromNode(childNode));
1071                 constraintsBuilder.setMinElements(max);
1072             } else if (childNode instanceof Min_elements_stmtContext) {
1073                 Integer min = Integer.valueOf(stringFromNode(childNode));
1074                 constraintsBuilder.setMinElements(min);
1075             } else if (childNode instanceof Must_stmtContext) {
1076                 String[] mustParams = parseMust((Must_stmtContext) childNode);
1077                 constraintsBuilder.addMustDefinition(mustParams[0],
1078                         mustParams[1], mustParams[2]);
1079             } else if (childNode instanceof Mandatory_stmtContext) {
1080                 for (int j = 0; j < childNode.getChildCount(); j++) {
1081                     ParseTree mandatoryTree = ctx.getChild(j);
1082                     if (mandatoryTree instanceof Mandatory_argContext) {
1083                         Boolean mandatory = Boolean
1084                                 .valueOf(stringFromNode(mandatoryTree));
1085                         constraintsBuilder.setMandatory(mandatory);
1086                     }
1087                 }
1088             } else if (childNode instanceof When_stmtContext) {
1089                 constraintsBuilder.addWhenCondition(stringFromNode(childNode));
1090             }
1091         }
1092     }
1093
1094 }