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