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