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