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