31ec7654233625adcae2652ec0083a37fb3d5b55
[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.yang.common.QName;
53 import org.opendaylight.controller.yang.model.api.RevisionAwareXPath;
54 import org.opendaylight.controller.yang.model.api.SchemaPath;
55 import org.opendaylight.controller.yang.model.api.Status;
56 import org.opendaylight.controller.yang.model.api.TypeDefinition;
57 import org.opendaylight.controller.yang.model.api.UnknownSchemaNode;
58 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition;
59 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition;
60 import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
61 import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
62 import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
63 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition.Bit;
64 import org.opendaylight.controller.yang.model.parser.builder.api.SchemaNodeBuilder;
65 import org.opendaylight.controller.yang.model.parser.builder.impl.ConstraintsBuilder;
66 import org.opendaylight.controller.yang.model.util.BaseConstraints;
67 import org.opendaylight.controller.yang.model.util.BinaryType;
68 import org.opendaylight.controller.yang.model.util.BitsType;
69 import org.opendaylight.controller.yang.model.util.EnumerationType;
70 import org.opendaylight.controller.yang.model.util.InstanceIdentifier;
71 import org.opendaylight.controller.yang.model.util.Leafref;
72 import org.opendaylight.controller.yang.model.util.RevisionAwareXPathImpl;
73 import org.opendaylight.controller.yang.model.util.StringType;
74 import org.opendaylight.controller.yang.model.util.UnknownType;
75 import org.opendaylight.controller.yang.model.util.YangTypesConverter;
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 = patternStringFromNode(ctx);
626         return BaseConstraints.patternConstraint(pattern, description,
627                 reference);
628     }
629
630     public static String patternStringFromNode(final Pattern_stmtContext treeNode) {
631         String result = "";
632         for (int i = 0; i < treeNode.getChildCount(); ++i) {
633             ParseTree child = treeNode.getChild(i);
634             if (child instanceof StringContext) {
635                 for(int j = 0; j < child.getChildCount(); j++) {
636                     if(j % 2 == 0) {
637                         String patternToken = child.getChild(j).getText();
638                         result += patternToken.substring(1, patternToken.length()-1);
639                     }
640                 }
641             }
642         }
643         return result;
644     }
645
646     private static Integer getFractionDigits(Type_body_stmtsContext ctx) {
647         for (int j = 0; j < ctx.getChildCount(); j++) {
648             ParseTree dec64specChild = ctx.getChild(j);
649             if (dec64specChild instanceof Decimal64_specificationContext) {
650                 return parseFractionDigits((Decimal64_specificationContext) dec64specChild);
651             }
652         }
653         return null;
654     }
655
656     private static Integer parseFractionDigits(
657             Decimal64_specificationContext ctx) {
658         for (int k = 0; k < ctx.getChildCount(); k++) {
659             ParseTree fdChild = ctx.getChild(k);
660             if (fdChild instanceof Fraction_digits_stmtContext) {
661                 return Integer.valueOf(stringFromNode(fdChild));
662             }
663         }
664         return null;
665     }
666
667     private static List<BitsTypeDefinition.Bit> getBits(
668             Type_body_stmtsContext ctx, List<String> actualPath, URI namespace,
669             Date revision, String prefix) {
670         List<BitsTypeDefinition.Bit> bits = new ArrayList<BitsTypeDefinition.Bit>();
671         for (int j = 0; j < ctx.getChildCount(); j++) {
672             ParseTree bitsSpecChild = ctx.getChild(j);
673             if (bitsSpecChild instanceof Bits_specificationContext) {
674                 for (int k = 0; k < bitsSpecChild.getChildCount(); k++) {
675                     ParseTree bitChild = bitsSpecChild.getChild(k);
676                     if (bitChild instanceof Bit_stmtContext) {
677                         bits.add(parseBit((Bit_stmtContext) bitChild,
678                                 actualPath, namespace, revision, prefix));
679                     }
680                 }
681             }
682         }
683         return bits;
684     }
685
686     private static boolean isRequireInstance(Type_body_stmtsContext ctx) {
687         for (int i = 0; i < ctx.getChildCount(); i++) {
688             ParseTree child = ctx.getChild(i);
689             if (child instanceof Require_instance_stmtContext) {
690                 for (int j = 0; j < child.getChildCount(); j++) {
691                     ParseTree reqArg = child.getChild(j);
692                     if (reqArg instanceof Require_instance_argContext) {
693                         return Boolean.valueOf(stringFromNode(reqArg));
694                     }
695                 }
696             }
697         }
698         return false;
699     }
700
701     private static BitsTypeDefinition.Bit parseBit(final Bit_stmtContext ctx,
702             List<String> actualPath, final URI namespace, final Date revision,
703             final String prefix) {
704         String name = stringFromNode(ctx);
705         final QName qname = new QName(namespace, revision, prefix, name);
706         Long position = null;
707
708         String description = null;
709         String reference = null;
710         Status status = Status.CURRENT;
711
712         Stack<String> bitPath = new Stack<String>();
713         bitPath.addAll(actualPath);
714         bitPath.add(name);
715
716         SchemaPath schemaPath = createActualSchemaPath(bitPath, namespace,
717                 revision, prefix);
718
719         for (int i = 0; i < ctx.getChildCount(); i++) {
720             ParseTree child = ctx.getChild(i);
721             if (child instanceof Position_stmtContext) {
722                 String positionStr = stringFromNode(child);
723                 position = Long.valueOf(positionStr);
724                 if (position < 0 || position > 4294967295L) {
725                     throw new IllegalArgumentException(
726                             "position value MUST be in the range 0 to 4294967295, but was: "
727                                     + position);
728                 }
729             } else if (child instanceof Description_stmtContext) {
730                 description = stringFromNode(child);
731             } else if (child instanceof Reference_stmtContext) {
732                 reference = stringFromNode(child);
733             } else if (child instanceof Status_stmtContext) {
734                 status = parseStatus((Status_stmtContext) child);
735             }
736         }
737
738         // TODO: extensionDefinitions
739         return createBit(qname, schemaPath, description, reference, status,
740                 null, position);
741     }
742
743     private static BitsTypeDefinition.Bit createBit(final QName qname,
744             final SchemaPath schemaPath, final String description,
745             final String reference, final Status status,
746             final List<UnknownSchemaNode> extensionDefinitions,
747             final Long position) {
748         return new BitsTypeDefinition.Bit() {
749
750             @Override
751             public QName getQName() {
752                 return qname;
753             }
754
755             @Override
756             public SchemaPath getPath() {
757                 return schemaPath;
758             }
759
760             @Override
761             public String getDescription() {
762                 return description;
763             }
764
765             @Override
766             public String getReference() {
767                 return reference;
768             }
769
770             @Override
771             public Status getStatus() {
772                 return status;
773             }
774
775             @Override
776             public List<UnknownSchemaNode> getUnknownSchemaNodes() {
777                 return extensionDefinitions;
778             }
779
780             @Override
781             public Long getPosition() {
782                 return position;
783             }
784
785             @Override
786             public String getName() {
787                 return qname.getLocalName();
788             }
789
790             @Override
791             public int hashCode() {
792                 final int prime = 31;
793                 int result = 1;
794                 result = prime * result
795                         + ((qname == null) ? 0 : qname.hashCode());
796                 result = prime * result
797                         + ((schemaPath == null) ? 0 : schemaPath.hashCode());
798                 result = prime * result
799                         + ((description == null) ? 0 : description.hashCode());
800                 result = prime * result
801                         + ((reference == null) ? 0 : reference.hashCode());
802                 result = prime * result
803                         + ((status == null) ? 0 : status.hashCode());
804                 result = prime * result
805                         + ((position == null) ? 0 : position.hashCode());
806                 result = prime
807                         * result
808                         + ((extensionDefinitions == null) ? 0
809                                 : extensionDefinitions.hashCode());
810                 return result;
811             }
812
813             @Override
814             public boolean equals(Object obj) {
815                 if (this == obj) {
816                     return true;
817                 }
818                 if (obj == null) {
819                     return false;
820                 }
821                 if (getClass() != obj.getClass()) {
822                     return false;
823                 }
824                 Bit other = (Bit) obj;
825                 if (qname == null) {
826                     if (other.getQName() != null) {
827                         return false;
828                     }
829                 } else if (!qname.equals(other.getQName())) {
830                     return false;
831                 }
832                 if (schemaPath == null) {
833                     if (other.getPath() != null) {
834                         return false;
835                     }
836                 } else if (!schemaPath.equals(other.getPath())) {
837                     return false;
838                 }
839                 if (description == null) {
840                     if (other.getDescription() != null) {
841                         return false;
842                     }
843                 } else if (!description.equals(other.getDescription())) {
844                     return false;
845                 }
846                 if (reference == null) {
847                     if (other.getReference() != null) {
848                         return false;
849                     }
850                 } else if (!reference.equals(other.getReference())) {
851                     return false;
852                 }
853                 if (status == null) {
854                     if (other.getStatus() != null) {
855                         return false;
856                     }
857                 } else if (!status.equals(other.getStatus())) {
858                     return false;
859                 }
860                 if (extensionDefinitions == null) {
861                     if (other.getUnknownSchemaNodes() != null) {
862                         return false;
863                     }
864                 } else if (!extensionDefinitions.equals(other
865                         .getUnknownSchemaNodes())) {
866                     return false;
867                 }
868                 if (position == null) {
869                     if (other.getPosition() != null) {
870                         return false;
871                     }
872                 } else if (!position.equals(other.getPosition())) {
873                     return false;
874                 }
875                 return true;
876             }
877
878             @Override
879             public String toString() {
880                 return Bit.class.getSimpleName() + "[name="
881                         + qname.getLocalName() + ", position=" + position + "]";
882             }
883         };
884     }
885
886     /**
887      * Parse orderedby statement.
888      *
889      * @param childNode
890      *            Ordered_by_stmtContext
891      * @return true, if orderedby contains value 'user' or false otherwise
892      */
893     public static boolean parseUserOrdered(Ordered_by_stmtContext childNode) {
894         boolean result = false;
895         for (int j = 0; j < childNode.getChildCount(); j++) {
896             ParseTree orderArg = childNode.getChild(j);
897             if (orderArg instanceof Ordered_by_argContext) {
898                 String orderStr = stringFromNode(orderArg);
899                 if (orderStr.equals("system")) {
900                     result = false;
901                 } else if (orderStr.equals("user")) {
902                     result = true;
903                 } else {
904                     logger.warn("Invalid 'orderedby' statement.");
905                 }
906             }
907         }
908         return result;
909     }
910
911     /**
912      * Parse given config context and return true if it contains string 'true',
913      * false otherwise.
914      *
915      * @param ctx
916      *            config context to parse.
917      * @return true if given context contains string 'true', false otherwise
918      */
919     public static boolean parseConfig(final Config_stmtContext ctx) {
920         if (ctx != null) {
921             for (int i = 0; i < ctx.getChildCount(); ++i) {
922                 final ParseTree configContext = ctx.getChild(i);
923                 if (configContext instanceof Config_argContext) {
924                     final String value = stringFromNode(configContext);
925                     if (value.equals("true")) {
926                         return true;
927                     }
928                 }
929             }
930         }
931         return false;
932     }
933
934     /**
935      * Parse given type body and creates UnknownType definition.
936      *
937      * @param typedefQName
938      *            qname of current type
939      * @param ctx
940      *            type body
941      * @return UnknownType object with constraints from parsed type body
942      */
943     public static TypeDefinition<?> parseUnknownTypeBody(QName typedefQName,
944             Type_body_stmtsContext ctx) {
945         UnknownType.Builder ut = new UnknownType.Builder(typedefQName);
946
947         if (ctx != null) {
948             List<RangeConstraint> rangeStatements = getRangeConstraints(ctx);
949             List<LengthConstraint> lengthStatements = getLengthConstraints(ctx);
950             List<PatternConstraint> patternStatements = getPatternConstraint(ctx);
951             Integer fractionDigits = getFractionDigits(ctx);
952
953             ut.rangeStatements(rangeStatements);
954             ut.lengthStatements(lengthStatements);
955             ut.patterns(patternStatements);
956             ut.fractionDigits(fractionDigits);
957         }
958
959         return ut.build();
960     }
961
962     /**
963      * Create TypeDefinition object based on given type name and type body.
964      *
965      * @param typeName
966      *            name of type
967      * @param typeBody
968      *            type body
969      * @param actualPath
970      *            current path in schema
971      * @param namespace
972      *            current namespace
973      * @param revision
974      *            current revision
975      * @param prefix
976      *            current prefix
977      * @return TypeDefinition object based on parsed values.
978      */
979     public static TypeDefinition<?> parseTypeBody(String typeName,
980             Type_body_stmtsContext typeBody, List<String> actualPath,
981             URI namespace, Date revision, String prefix) {
982         TypeDefinition<?> type = null;
983
984         List<RangeConstraint> rangeStatements = getRangeConstraints(typeBody);
985         Integer fractionDigits = getFractionDigits(typeBody);
986         List<LengthConstraint> lengthStatements = getLengthConstraints(typeBody);
987         List<PatternConstraint> patternStatements = getPatternConstraint(typeBody);
988         List<EnumTypeDefinition.EnumPair> enumConstants = getEnumConstants(typeBody, actualPath, namespace, revision, prefix);
989
990         if (typeName.equals("decimal64")) {
991             type = YangTypesConverter.javaTypeForBaseYangDecimal64Type(
992                     rangeStatements, fractionDigits);
993         } else if (typeName.startsWith("int")) {
994             type = YangTypesConverter.javaTypeForBaseYangSignedIntegerType(typeName,
995                     rangeStatements);
996         } else if(typeName.startsWith("uint")) {
997             type = YangTypesConverter.javaTypeForBaseYangUnsignedIntegerType(typeName,
998                     rangeStatements);
999         } else if (typeName.equals("enumeration")) {
1000             type = new EnumerationType(enumConstants);
1001         } else if (typeName.equals("string")) {
1002             type = new StringType(lengthStatements, patternStatements);
1003         } else if (typeName.equals("bits")) {
1004             type = new BitsType(getBits(typeBody, actualPath, namespace,
1005                     revision, prefix));
1006         } else if (typeName.equals("leafref")) {
1007             final String path = parseLeafrefPath(typeBody);
1008             final boolean absolute = path.startsWith("/");
1009             RevisionAwareXPath xpath = new RevisionAwareXPathImpl(path,
1010                     absolute);
1011             type = new Leafref(xpath);
1012         } else if (typeName.equals("binary")) {
1013             type = new BinaryType(null, lengthStatements, null);
1014         } else if (typeName.equals("instance-identifier")) {
1015             boolean requireInstance = isRequireInstance(typeBody);
1016             type = new InstanceIdentifier(null, requireInstance);
1017         }
1018         return type;
1019     }
1020
1021     private static String parseLeafrefPath(Type_body_stmtsContext ctx) {
1022         for (int i = 0; i < ctx.getChildCount(); i++) {
1023             ParseTree child = ctx.getChild(i);
1024             if (child instanceof Leafref_specificationContext) {
1025                 for (int j = 0; j < child.getChildCount(); j++) {
1026                     ParseTree leafRefSpec = child.getChild(j);
1027                     if (leafRefSpec instanceof Path_stmtContext) {
1028                         return stringFromNode(leafRefSpec);
1029                     }
1030                 }
1031             }
1032         }
1033         return null;
1034     }
1035
1036     /**
1037      * Internal helper method for parsing Must_stmtContext.
1038      *
1039      * @param ctx
1040      *            Must_stmtContext
1041      * @return an array of strings with following fields: [0] must text [1]
1042      *         description [2] reference
1043      */
1044     public static String[] parseMust(YangParser.Must_stmtContext ctx) {
1045         String[] params = new String[3];
1046
1047         String mustText = "";
1048         String description = null;
1049         String reference = null;
1050         for (int i = 0; i < ctx.getChildCount(); ++i) {
1051             ParseTree child = ctx.getChild(i);
1052             if (child instanceof StringContext) {
1053                 final StringContext context = (StringContext) child;
1054                 for (int j = 0; j < context.getChildCount(); j++) {
1055                     String mustPart = context.getChild(j).getText();
1056                     if (j == 0) {
1057                         mustText += mustPart
1058                                 .substring(0, mustPart.length() - 1);
1059                         continue;
1060                     }
1061                     if (j % 2 == 0) {
1062                         mustText += mustPart.substring(1);
1063                     }
1064                 }
1065             } else if (child instanceof Description_stmtContext) {
1066                 description = stringFromNode(child);
1067             } else if (child instanceof Reference_stmtContext) {
1068                 reference = stringFromNode(child);
1069             }
1070         }
1071         params[0] = mustText;
1072         params[1] = description;
1073         params[2] = reference;
1074
1075         return params;
1076     }
1077
1078     /**
1079      * Parse given tree and set constraints to given builder.
1080      *
1081      * @param ctx
1082      *            Context to search.
1083      * @param constraintsBuilder
1084      *            ConstraintsBuilder to fill.
1085      */
1086     public static void parseConstraints(ParseTree ctx,
1087             ConstraintsBuilder constraintsBuilder) {
1088         for (int i = 0; i < ctx.getChildCount(); ++i) {
1089             final ParseTree childNode = ctx.getChild(i);
1090             if (childNode instanceof Max_elements_stmtContext) {
1091                 Integer max = Integer.valueOf(stringFromNode(childNode));
1092                 constraintsBuilder.setMinElements(max);
1093             } else if (childNode instanceof Min_elements_stmtContext) {
1094                 Integer min = Integer.valueOf(stringFromNode(childNode));
1095                 constraintsBuilder.setMinElements(min);
1096             } else if (childNode instanceof Must_stmtContext) {
1097                 String[] mustParams = parseMust((Must_stmtContext) childNode);
1098                 constraintsBuilder.addMustDefinition(mustParams[0],
1099                         mustParams[1], mustParams[2]);
1100             } else if (childNode instanceof Mandatory_stmtContext) {
1101                 for (int j = 0; j < childNode.getChildCount(); j++) {
1102                     ParseTree mandatoryTree = ctx.getChild(j);
1103                     if (mandatoryTree instanceof Mandatory_argContext) {
1104                         Boolean mandatory = Boolean
1105                                 .valueOf(stringFromNode(mandatoryTree));
1106                         constraintsBuilder.setMandatory(mandatory);
1107                     }
1108                 }
1109             } else if (childNode instanceof When_stmtContext) {
1110                 constraintsBuilder.addWhenCondition(stringFromNode(childNode));
1111             }
1112         }
1113     }
1114
1115 }