Implemented ordering of yang module data nodes. Added Comparators utility class.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / yang-model-parser-impl / src / main / java / org / opendaylight / controller / yang / parser / util / ParserUtils.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 package org.opendaylight.controller.yang.parser.util;
9
10 import java.util.ArrayList;
11 import java.util.Date;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.TreeMap;
16
17 import org.opendaylight.controller.yang.common.QName;
18 import org.opendaylight.controller.yang.model.api.AnyXmlSchemaNode;
19 import org.opendaylight.controller.yang.model.api.ChoiceNode;
20 import org.opendaylight.controller.yang.model.api.ConstraintDefinition;
21 import org.opendaylight.controller.yang.model.api.ContainerSchemaNode;
22 import org.opendaylight.controller.yang.model.api.DataNodeContainer;
23 import org.opendaylight.controller.yang.model.api.DataSchemaNode;
24 import org.opendaylight.controller.yang.model.api.GroupingDefinition;
25 import org.opendaylight.controller.yang.model.api.LeafListSchemaNode;
26 import org.opendaylight.controller.yang.model.api.LeafSchemaNode;
27 import org.opendaylight.controller.yang.model.api.ListSchemaNode;
28 import org.opendaylight.controller.yang.model.api.Module;
29 import org.opendaylight.controller.yang.model.api.ModuleImport;
30 import org.opendaylight.controller.yang.model.api.MustDefinition;
31 import org.opendaylight.controller.yang.model.api.NotificationDefinition;
32 import org.opendaylight.controller.yang.model.api.RevisionAwareXPath;
33 import org.opendaylight.controller.yang.model.api.SchemaContext;
34 import org.opendaylight.controller.yang.model.api.SchemaNode;
35 import org.opendaylight.controller.yang.model.api.SchemaPath;
36 import org.opendaylight.controller.yang.model.api.TypeDefinition;
37 import org.opendaylight.controller.yang.model.api.UnknownSchemaNode;
38 import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;
39 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition;
40 import org.opendaylight.controller.yang.model.api.type.BooleanTypeDefinition;
41 import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;
42 import org.opendaylight.controller.yang.model.api.type.EmptyTypeDefinition;
43 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition;
44 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition.EnumPair;
45 import org.opendaylight.controller.yang.model.api.type.IdentityrefTypeDefinition;
46 import org.opendaylight.controller.yang.model.api.type.InstanceIdentifierTypeDefinition;
47 import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;
48 import org.opendaylight.controller.yang.model.api.type.LeafrefTypeDefinition;
49 import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
50 import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
51 import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
52 import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;
53 import org.opendaylight.controller.yang.model.api.type.UnionTypeDefinition;
54 import org.opendaylight.controller.yang.model.api.type.UnsignedIntegerTypeDefinition;
55 import org.opendaylight.controller.yang.model.util.BinaryType;
56 import org.opendaylight.controller.yang.model.util.BitsType;
57 import org.opendaylight.controller.yang.model.util.BooleanType;
58 import org.opendaylight.controller.yang.model.util.Decimal64;
59 import org.opendaylight.controller.yang.model.util.EmptyType;
60 import org.opendaylight.controller.yang.model.util.EnumerationType;
61 import org.opendaylight.controller.yang.model.util.ExtendedType;
62 import org.opendaylight.controller.yang.model.util.IdentityrefType;
63 import org.opendaylight.controller.yang.model.util.InstanceIdentifier;
64 import org.opendaylight.controller.yang.model.util.Int16;
65 import org.opendaylight.controller.yang.model.util.Int32;
66 import org.opendaylight.controller.yang.model.util.Int64;
67 import org.opendaylight.controller.yang.model.util.Int8;
68 import org.opendaylight.controller.yang.model.util.Leafref;
69 import org.opendaylight.controller.yang.model.util.StringType;
70 import org.opendaylight.controller.yang.model.util.Uint16;
71 import org.opendaylight.controller.yang.model.util.Uint32;
72 import org.opendaylight.controller.yang.model.util.Uint64;
73 import org.opendaylight.controller.yang.model.util.Uint8;
74 import org.opendaylight.controller.yang.model.util.UnionType;
75 import org.opendaylight.controller.yang.model.util.UnknownType;
76 import org.opendaylight.controller.yang.parser.builder.api.AugmentationSchemaBuilder;
77 import org.opendaylight.controller.yang.parser.builder.api.AugmentationTargetBuilder;
78 import org.opendaylight.controller.yang.parser.builder.api.Builder;
79 import org.opendaylight.controller.yang.parser.builder.api.DataNodeContainerBuilder;
80 import org.opendaylight.controller.yang.parser.builder.api.DataSchemaNodeBuilder;
81 import org.opendaylight.controller.yang.parser.builder.api.GroupingBuilder;
82 import org.opendaylight.controller.yang.parser.builder.api.GroupingMember;
83 import org.opendaylight.controller.yang.parser.builder.api.SchemaNodeBuilder;
84 import org.opendaylight.controller.yang.parser.builder.api.TypeAwareBuilder;
85 import org.opendaylight.controller.yang.parser.builder.api.TypeDefinitionBuilder;
86 import org.opendaylight.controller.yang.parser.builder.api.UsesNodeBuilder;
87 import org.opendaylight.controller.yang.parser.builder.impl.AnyXmlBuilder;
88 import org.opendaylight.controller.yang.parser.builder.impl.ChoiceBuilder;
89 import org.opendaylight.controller.yang.parser.builder.impl.ChoiceBuilder.ChoiceNodeImpl;
90 import org.opendaylight.controller.yang.parser.builder.impl.ChoiceCaseBuilder;
91 import org.opendaylight.controller.yang.parser.builder.impl.ChoiceCaseBuilder.ChoiceCaseNodeImpl;
92 import org.opendaylight.controller.yang.parser.builder.impl.ConstraintsBuilder;
93 import org.opendaylight.controller.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
94 import org.opendaylight.controller.yang.parser.builder.impl.ContainerSchemaNodeBuilder.ContainerSchemaNodeImpl;
95 import org.opendaylight.controller.yang.parser.builder.impl.GroupingBuilderImpl;
96 import org.opendaylight.controller.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
97 import org.opendaylight.controller.yang.parser.builder.impl.LeafSchemaNodeBuilder;
98 import org.opendaylight.controller.yang.parser.builder.impl.ListSchemaNodeBuilder;
99 import org.opendaylight.controller.yang.parser.builder.impl.ListSchemaNodeBuilder.ListSchemaNodeImpl;
100 import org.opendaylight.controller.yang.parser.builder.impl.ModuleBuilder;
101 import org.opendaylight.controller.yang.parser.builder.impl.NotificationBuilder;
102 import org.opendaylight.controller.yang.parser.builder.impl.NotificationBuilder.NotificationDefinitionImpl;
103 import org.opendaylight.controller.yang.parser.builder.impl.RpcDefinitionBuilder;
104 import org.opendaylight.controller.yang.parser.builder.impl.TypeDefinitionBuilderImpl;
105 import org.opendaylight.controller.yang.parser.builder.impl.UnionTypeBuilder;
106 import org.opendaylight.controller.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
107
108 public final class ParserUtils {
109
110     private ParserUtils() {
111     }
112
113     /**
114      * Create new SchemaPath from given path and name.
115      *
116      * Append new qname to schema path created from name argument. New QName
117      * gets namespace, revision and prefix same as last qname in current schema
118      * path.
119      *
120      * @param schemaPath
121      * @param name
122      * @return
123      */
124     public static SchemaPath createSchemaPath(SchemaPath schemaPath, String name) {
125         List<QName> path = new ArrayList<QName>(schemaPath.getPath());
126         QName last = path.get(path.size() - 1);
127         QName newQName = new QName(last.getNamespace(), last.getRevision(), last.getPrefix(), name);
128         path.add(newQName);
129         return new SchemaPath(path, schemaPath.isAbsolute());
130     }
131
132     /**
133      * Get module import referenced by given prefix.
134      *
135      * @param builder
136      *            module to search
137      * @param prefix
138      *            prefix associated with import
139      * @return ModuleImport based on given prefix
140      */
141     public static ModuleImport getModuleImport(final ModuleBuilder builder, final String prefix) {
142         ModuleImport moduleImport = null;
143         for (ModuleImport mi : builder.getModuleImports()) {
144             if (mi.getPrefix().equals(prefix)) {
145                 moduleImport = mi;
146                 break;
147             }
148         }
149         return moduleImport;
150     }
151
152     /**
153      * Find dependent module based on given prefix
154      *
155      * @param modules
156      *            all available modules
157      * @param module
158      *            current module
159      * @param prefix
160      *            target module prefix
161      * @param line
162      *            current line in yang model
163      * @return
164      */
165     public static ModuleBuilder findDependentModuleBuilder(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
166             final ModuleBuilder module, final String prefix, final int line) {
167         ModuleBuilder dependentModule = null;
168         Date dependentModuleRevision = null;
169
170         if (prefix.equals(module.getPrefix())) {
171             dependentModule = module;
172         } else {
173             final ModuleImport dependentModuleImport = getModuleImport(module, prefix);
174             if (dependentModuleImport == null) {
175                 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
176             }
177             final String dependentModuleName = dependentModuleImport.getModuleName();
178             dependentModuleRevision = dependentModuleImport.getRevision();
179
180             final TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
181             if (moduleBuildersByRevision == null) {
182                 return null;
183             }
184             if (dependentModuleRevision == null) {
185                 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
186             } else {
187                 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
188             }
189         }
190         return dependentModule;
191     }
192
193     /**
194      * Find module from context based on prefix.
195      *
196      * @param context
197      *            schema context
198      * @param currentModule
199      *            current module
200      * @param prefix
201      *            current prefix used to reference dependent module
202      * @param line
203      *            current line in yang model
204      * @return module based on given prefix if found in context, null otherwise
205      */
206     public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
207             final String prefix, final int line) {
208         TreeMap<Date, Module> modulesByRevision = new TreeMap<Date, Module>();
209
210         Date dependentModuleRevision = null;
211
212         final ModuleImport dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
213         if (dependentModuleImport == null) {
214             throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
215         }
216         final String dependentModuleName = dependentModuleImport.getModuleName();
217         dependentModuleRevision = dependentModuleImport.getRevision();
218
219         for (Module contextModule : context.getModules()) {
220             if (contextModule.getName().equals(dependentModuleName)) {
221                 Date revision = contextModule.getRevision();
222                 if (revision == null) {
223                     revision = new Date(0L);
224                 }
225                 modulesByRevision.put(revision, contextModule);
226                 break;
227             }
228         }
229
230         Module result = null;
231         if (dependentModuleRevision == null) {
232             result = modulesByRevision.get(modulesByRevision.firstKey());
233         } else {
234             result = modulesByRevision.get(dependentModuleRevision);
235         }
236
237         return result;
238     }
239
240     /**
241      * Find grouping by name.
242      *
243      * @param groupings
244      *            collection of grouping builders to search
245      * @param name
246      *            name of grouping
247      * @return grouping with given name if present in collection, null otherwise
248      */
249     public static GroupingBuilder findGroupingBuilder(Set<GroupingBuilder> groupings, String name) {
250         for (GroupingBuilder grouping : groupings) {
251             if (grouping.getQName().getLocalName().equals(name)) {
252                 return grouping;
253             }
254         }
255         return null;
256     }
257
258     /**
259      * Find grouping by name.
260      *
261      * @param groupings
262      *            collection of grouping definitions to search
263      * @param name
264      *            name of grouping
265      * @return grouping with given name if present in collection, null otherwise
266      */
267     public static GroupingDefinition findGroupingDefinition(Set<GroupingDefinition> groupings, String name) {
268         for (GroupingDefinition grouping : groupings) {
269             if (grouping.getQName().getLocalName().equals(name)) {
270                 return grouping;
271             }
272         }
273         return null;
274     }
275
276     /**
277      * Search types for type with given name.
278      *
279      * @param types
280      *            types to search
281      * @param name
282      *            name of type
283      * @return type with given name if present in collection, null otherwise
284      */
285     public static TypeDefinitionBuilder findTypedefBuilderByName(Set<TypeDefinitionBuilder> types, String name) {
286         for (TypeDefinitionBuilder td : types) {
287             if (td.getQName().getLocalName().equals(name)) {
288                 return td;
289             }
290         }
291         return null;
292     }
293
294     /**
295      * Find type by name.
296      *
297      * @param types
298      *            collection of types
299      * @param typeName
300      *            type name
301      * @return type with given name if it is present in collection, null
302      *         otherwise
303      */
304     public static TypeDefinition<?> findTypeByName(Set<TypeDefinition<?>> types, String typeName) {
305         for (TypeDefinition<?> type : types) {
306             if (type.getQName().getLocalName().equals(typeName)) {
307                 return type;
308             }
309         }
310         return null;
311     }
312
313     /**
314      * Parse uses path.
315      *
316      * @param usesPath
317      *            as String
318      * @return SchemaPath from given String
319      */
320     public static SchemaPath parseUsesPath(final String usesPath) {
321         final boolean absolute = usesPath.startsWith("/");
322         final String[] splittedPath = usesPath.split("/");
323         final List<QName> path = new ArrayList<QName>();
324         QName name;
325         for (String pathElement : splittedPath) {
326             if (pathElement.length() > 0) {
327                 final String[] splittedElement = pathElement.split(":");
328                 if (splittedElement.length == 1) {
329                     name = new QName(null, null, null, splittedElement[0]);
330                 } else {
331                     name = new QName(null, null, splittedElement[0], splittedElement[1]);
332                 }
333                 path.add(name);
334             }
335         }
336         return new SchemaPath(path, absolute);
337     }
338
339     /**
340      * Check if node is present in refine nodes.
341      *
342      * @param nodeQName
343      *            qname of node
344      * @param refineNodes
345      *            collections of refined nodes
346      * @return true, if node with given qname was found, false otherwise
347      */
348     public static SchemaNodeBuilder getRefined(QName nodeQName, List<SchemaNodeBuilder> refineNodes) {
349         for (SchemaNodeBuilder rn : refineNodes) {
350             if (rn.getQName().equals(nodeQName)) {
351                 return rn;
352             }
353         }
354         return null;
355     }
356
357     /**
358      * Pull restriction from type and add them to constraints.
359      *
360      * @param type
361      * @param constraints
362      */
363     public static void mergeConstraints(final TypeDefinition<?> type, final TypeConstraints constraints) {
364         if (type instanceof DecimalTypeDefinition) {
365             constraints.addRanges(((DecimalTypeDefinition) type).getRangeStatements());
366             constraints.addFractionDigits(((DecimalTypeDefinition) type).getFractionDigits());
367         } else if (type instanceof IntegerTypeDefinition) {
368             constraints.addRanges(((IntegerTypeDefinition) type).getRangeStatements());
369         } else if (type instanceof StringTypeDefinition) {
370             constraints.addPatterns(((StringTypeDefinition) type).getPatterns());
371             constraints.addLengths(((StringTypeDefinition) type).getLengthStatements());
372         } else if (type instanceof BinaryTypeDefinition) {
373             constraints.addLengths(((BinaryTypeDefinition) type).getLengthConstraints());
374         }
375     }
376
377     /**
378      * Find node in grouping by name.
379      *
380      * @param grouping
381      *            grouping to search
382      * @param refineNodeName
383      *            name of node
384      * @return builder of node with given name if present in grouping, null
385      *         otherwise
386      */
387     public static Builder findRefineTargetBuilder(final GroupingBuilder grouping, final String refineNodeName) {
388         // search child nodes
389         Builder result = grouping.getChildNode(refineNodeName);
390         // search groupings
391         if (result == null) {
392             Set<GroupingBuilder> grps = grouping.getGroupingBuilders();
393             for (GroupingBuilder gr : grps) {
394                 if (gr.getQName().getLocalName().equals(refineNodeName)) {
395                     result = gr;
396                     break;
397                 }
398             }
399         }
400         // search typedefs
401         if (result == null) {
402             Set<TypeDefinitionBuilder> typedefs = grouping.getTypeDefinitionBuilders();
403             for (TypeDefinitionBuilder typedef : typedefs) {
404                 if (typedef.getQName().getLocalName().equals(refineNodeName)) {
405                     result = typedef;
406                     break;
407                 }
408             }
409         }
410         return result;
411     }
412
413     /**
414      * Find node in grouping by name.
415      *
416      * @param builder
417      *            grouping to search
418      * @param refineNodeName
419      *            name of node
420      * @return node with given name if present in grouping, null otherwise
421      */
422     public static Object findRefineTargetNode(final GroupingDefinition builder, final String refineNodeName) {
423         Object result = builder.getDataChildByName(refineNodeName);
424         if (result == null) {
425             Set<GroupingDefinition> grps = builder.getGroupings();
426             for (GroupingDefinition gr : grps) {
427                 if (gr.getQName().getLocalName().equals(refineNodeName)) {
428                     result = gr;
429                     break;
430                 }
431             }
432         }
433         if (result == null) {
434             Set<TypeDefinition<?>> typedefs = builder.getTypeDefinitions();
435             for (TypeDefinition<?> typedef : typedefs) {
436                 if (typedef.getQName().getLocalName().equals(refineNodeName)) {
437                     result = typedef;
438                     break;
439                 }
440             }
441         }
442         return result;
443     }
444
445     /**
446      * Add all augment's child nodes to given target.
447      *
448      * @param augment
449      *            builder of augment statement
450      * @param target
451      *            augmentation target node
452      */
453     public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
454         boolean usesAugment = augment.getParent() instanceof UsesNodeBuilder;
455         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
456             builder.setAugmenting(true);
457             if (usesAugment) {
458                 if (builder instanceof GroupingMember) {
459                     ((GroupingMember) builder).setAddedByUses(true);
460                 }
461             }
462             correctAugmentChildPath(builder, target.getPath());
463             target.addChildNode(builder);
464         }
465     }
466
467     /**
468      * Add all augment's child nodes to given target.
469      *
470      * @param augment
471      *            builder of augment statement
472      * @param target
473      *            augmentation target choice node
474      */
475     public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
476         boolean usesAugment = augment.getParent() instanceof UsesNodeBuilder;
477         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
478             builder.setAugmenting(true);
479             if (usesAugment) {
480                 if (builder instanceof GroupingMember) {
481                     ((GroupingMember) builder).setAddedByUses(true);
482                 }
483             }
484             correctAugmentChildPath(builder, target.getPath());
485             target.addChildNode(builder);
486         }
487     }
488
489     private static void correctAugmentChildPath(final DataSchemaNodeBuilder childNode, final SchemaPath parentSchemaPath) {
490         // set correct path
491         List<QName> targetNodePath = new ArrayList<QName>(parentSchemaPath.getPath());
492         targetNodePath.add(childNode.getQName());
493         childNode.setPath(new SchemaPath(targetNodePath, true));
494
495         // set correct path for all child nodes
496         if (childNode instanceof DataNodeContainerBuilder) {
497             DataNodeContainerBuilder dataNodeContainer = (DataNodeContainerBuilder) childNode;
498             for (DataSchemaNodeBuilder child : dataNodeContainer.getChildNodeBuilders()) {
499                 correctAugmentChildPath(child, childNode.getPath());
500             }
501         }
502
503         // set correct path for all cases
504         if(childNode instanceof ChoiceBuilder) {
505             ChoiceBuilder choiceBuilder = (ChoiceBuilder) childNode;
506             for (ChoiceCaseBuilder choiceCaseBuilder : choiceBuilder.getCases()) {
507                 correctAugmentChildPath(choiceCaseBuilder, childNode.getPath());
508             }
509         }
510
511         // if node can contains type, correct path for this type too
512         if (childNode instanceof TypeAwareBuilder) {
513             TypeAwareBuilder nodeBuilder = (TypeAwareBuilder) childNode;
514             correctTypeAwareNodePath(nodeBuilder, parentSchemaPath);
515         }
516     }
517
518     /**
519      * Repair schema path of node type.
520      *
521      * @param node
522      *            node which contains type statement
523      * @param parentSchemaPath
524      *            schema path of parent node
525      */
526     private static void correctTypeAwareNodePath(final TypeAwareBuilder node, final SchemaPath parentSchemaPath) {
527         final QName nodeBuilderQName = node.getQName();
528         final TypeDefinition<?> nodeType = node.getType();
529
530         Integer fd = null;
531         List<LengthConstraint> lengths = null;
532         List<PatternConstraint> patterns = null;
533         List<RangeConstraint> ranges = null;
534
535         if (nodeType != null) {
536             if (nodeType instanceof ExtendedType) {
537                 ExtendedType et = (ExtendedType) nodeType;
538                 if (nodeType.getQName().getLocalName().equals(nodeType.getBaseType().getQName().getLocalName())) {
539                     fd = et.getFractionDigits();
540                     lengths = et.getLengths();
541                     patterns = et.getPatterns();
542                     ranges = et.getRanges();
543                     if (!hasConstraints(fd, lengths, patterns, ranges)) {
544                         return;
545                     }
546                 }
547             }
548             TypeDefinition<?> newType = createCorrectTypeDefinition(parentSchemaPath, nodeBuilderQName, nodeType);
549             node.setType(newType);
550         } else {
551             TypeDefinitionBuilder nodeBuilderTypedef = node.getTypedef();
552
553             fd = nodeBuilderTypedef.getFractionDigits();
554             lengths = nodeBuilderTypedef.getLengths();
555             patterns = nodeBuilderTypedef.getPatterns();
556             ranges = nodeBuilderTypedef.getRanges();
557
558             String tdbTypeName = nodeBuilderTypedef.getQName().getLocalName();
559             String baseTypeName = null;
560             if (nodeBuilderTypedef.getType() == null) {
561                 baseTypeName = nodeBuilderTypedef.getTypedef().getQName().getLocalName();
562             } else {
563                 baseTypeName = nodeBuilderTypedef.getType().getQName().getLocalName();
564             }
565             if (!(tdbTypeName.equals(baseTypeName))) {
566                 return;
567             }
568
569             if (!hasConstraints(fd, lengths, patterns, ranges)) {
570                 return;
571             }
572
573             SchemaPath newSchemaPath = createNewSchemaPath(nodeBuilderTypedef.getPath(), nodeBuilderQName,
574                     nodeBuilderTypedef.getQName());
575             nodeBuilderTypedef.setPath(newSchemaPath);
576         }
577     }
578
579     /**
580      * Check if there are some constraints.
581      *
582      * @param fd
583      *            fraction digits
584      * @param lengths
585      *            length constraints
586      * @param patterns
587      *            pattern constraints
588      * @param ranges
589      *            range constraints
590      * @return true, if any of constraints are present, false otherwise
591      */
592     private static boolean hasConstraints(final Integer fd, final List<LengthConstraint> lengths,
593             final List<PatternConstraint> patterns, final List<RangeConstraint> ranges) {
594         if (fd == null && (lengths == null || lengths.isEmpty()) && (patterns == null || patterns.isEmpty())
595                 && (ranges == null || ranges.isEmpty())) {
596             return false;
597         } else {
598             return true;
599         }
600     }
601
602     private static TypeDefinition<?> createCorrectTypeDefinition(SchemaPath parentSchemaPath, QName nodeQName,
603             TypeDefinition<?> nodeType) {
604         QName nodeTypeQName = nodeType.getQName();
605         SchemaPath newSchemaPath = createNewSchemaPath(parentSchemaPath, nodeQName, nodeTypeQName);
606         TypeDefinition<?> result = null;
607
608         if (nodeType != null) {
609             if (nodeType instanceof BinaryTypeDefinition) {
610                 BinaryTypeDefinition binType = (BinaryTypeDefinition) nodeType;
611
612                 // List<Byte> bytes = (List<Byte>) binType.getDefaultValue();
613                 // workaround to get rid of 'Unchecked cast' warning
614                 List<Byte> bytes = new ArrayList<Byte>();
615                 Object defaultValue = binType.getDefaultValue();
616                 if (defaultValue instanceof List) {
617                     for (Object o : List.class.cast(defaultValue)) {
618                         if (o instanceof Byte) {
619                             bytes.add((Byte) o);
620                         }
621                     }
622                 }
623                 result = new BinaryType(newSchemaPath, bytes);
624             } else if (nodeType instanceof BitsTypeDefinition) {
625                 BitsTypeDefinition bitsType = (BitsTypeDefinition) nodeType;
626                 result = new BitsType(newSchemaPath, bitsType.getBits());
627             } else if (nodeType instanceof BooleanTypeDefinition) {
628                 result = new BooleanType(newSchemaPath);
629             } else if (nodeType instanceof DecimalTypeDefinition) {
630                 DecimalTypeDefinition decimalType = (DecimalTypeDefinition) nodeType;
631                 result = new Decimal64(newSchemaPath, decimalType.getFractionDigits());
632             } else if (nodeType instanceof EmptyTypeDefinition) {
633                 result = new EmptyType(newSchemaPath);
634             } else if (nodeType instanceof EnumTypeDefinition) {
635                 EnumTypeDefinition enumType = (EnumTypeDefinition) nodeType;
636                 result = new EnumerationType(newSchemaPath, (EnumPair) enumType.getDefaultValue(), enumType.getValues());
637             } else if (nodeType instanceof IdentityrefTypeDefinition) {
638                 IdentityrefTypeDefinition idrefType = (IdentityrefTypeDefinition) nodeType;
639                 result = new IdentityrefType(idrefType.getIdentity(), newSchemaPath);
640             } else if (nodeType instanceof InstanceIdentifierTypeDefinition) {
641                 InstanceIdentifierTypeDefinition instIdType = (InstanceIdentifierTypeDefinition) nodeType;
642                 return new InstanceIdentifier(newSchemaPath, instIdType.getPathStatement(),
643                         instIdType.requireInstance());
644             } else if (nodeType instanceof StringTypeDefinition) {
645                 result = createNewStringType(parentSchemaPath, nodeQName, (StringTypeDefinition) nodeType);
646             } else if (nodeType instanceof IntegerTypeDefinition) {
647                 result = createNewIntType(parentSchemaPath, nodeQName, (IntegerTypeDefinition) nodeType);
648             } else if (nodeType instanceof UnsignedIntegerTypeDefinition) {
649                 result = createNewUintType(parentSchemaPath, nodeQName, (UnsignedIntegerTypeDefinition) nodeType);
650             } else if (nodeType instanceof LeafrefTypeDefinition) {
651                 result = new Leafref(newSchemaPath, ((LeafrefTypeDefinition) nodeType).getPathStatement());
652             } else if (nodeType instanceof UnionTypeDefinition) {
653                 UnionTypeDefinition unionType = (UnionTypeDefinition) nodeType;
654                 return new UnionType(newSchemaPath, unionType.getTypes());
655             } else if (nodeType instanceof ExtendedType) {
656                 ExtendedType extType = (ExtendedType) nodeType;
657                 result = createNewExtendedType(extType, newSchemaPath);
658             }
659         }
660         return result;
661     }
662
663     /**
664      * Create new ExtendedType based on given type and with schema path.
665      *
666      * @param newPath
667      *            schema path for new type
668      * @param oldType
669      *            type based
670      * @return
671      */
672     private static ExtendedType createNewExtendedType(final ExtendedType oldType, final SchemaPath newPath) {
673         QName qname = oldType.getQName();
674         TypeDefinition<?> baseType = oldType.getBaseType();
675         String desc = oldType.getDescription();
676         String ref = oldType.getReference();
677         ExtendedType.Builder builder = new ExtendedType.Builder(qname, baseType, desc, ref, newPath);
678         builder.status(oldType.getStatus());
679         builder.lengths(oldType.getLengths());
680         builder.patterns(oldType.getPatterns());
681         builder.ranges(oldType.getRanges());
682         builder.fractionDigits(oldType.getFractionDigits());
683         builder.unknownSchemaNodes(oldType.getUnknownSchemaNodes());
684         return builder.build();
685     }
686
687     private static StringTypeDefinition createNewStringType(final SchemaPath schemaPath, final QName nodeQName,
688             final StringTypeDefinition nodeType) {
689         final List<QName> path = schemaPath.getPath();
690         final List<QName> newPath = new ArrayList<QName>(path);
691         newPath.add(nodeQName);
692         newPath.add(nodeType.getQName());
693         final SchemaPath newSchemaPath = new SchemaPath(newPath, schemaPath.isAbsolute());
694         return new StringType(newSchemaPath);
695     }
696
697     private static IntegerTypeDefinition createNewIntType(final SchemaPath schemaPath, final QName nodeQName,
698             final IntegerTypeDefinition type) {
699         final QName typeQName = type.getQName();
700         final SchemaPath newSchemaPath = createNewSchemaPath(schemaPath, nodeQName, typeQName);
701         final String localName = typeQName.getLocalName();
702
703         if ("int8".equals(localName)) {
704             return new Int8(newSchemaPath);
705         } else if ("int16".equals(localName)) {
706             return new Int16(newSchemaPath);
707         } else if ("int32".equals(localName)) {
708             return new Int32(newSchemaPath);
709         } else if ("int64".equals(localName)) {
710             return new Int64(newSchemaPath);
711         } else {
712             return null;
713         }
714     }
715
716     private static UnsignedIntegerTypeDefinition createNewUintType(final SchemaPath schemaPath, final QName nodeQName,
717             final UnsignedIntegerTypeDefinition type) {
718         final QName typeQName = type.getQName();
719         final SchemaPath newSchemaPath = createNewSchemaPath(schemaPath, nodeQName, typeQName);
720         final String localName = typeQName.getLocalName();
721
722         if ("uint8".equals(localName)) {
723             return new Uint8(newSchemaPath);
724         } else if ("uint16".equals(localName)) {
725             return new Uint16(newSchemaPath);
726         } else if ("uint32".equals(localName)) {
727             return new Uint32(newSchemaPath);
728         } else if ("uint64".equals(localName)) {
729             return new Uint64(newSchemaPath);
730         } else {
731             return null;
732         }
733     }
734
735     private static SchemaPath createNewSchemaPath(final SchemaPath schemaPath, final QName currentQName,
736             final QName qname) {
737         List<QName> newPath = new ArrayList<QName>(schemaPath.getPath());
738         newPath.add(currentQName);
739         newPath.add(qname);
740         return new SchemaPath(newPath, schemaPath.isAbsolute());
741     }
742
743     /**
744      * Create LeafSchemaNodeBuilder from given LeafSchemaNode.
745      *
746      * @param leaf
747      *            leaf from which to create builder
748      * @param line
749      *            line in module
750      * @return builder object from leaf
751      */
752     public static LeafSchemaNodeBuilder createLeafBuilder(LeafSchemaNode leaf, int line) {
753         final LeafSchemaNodeBuilder builder = new LeafSchemaNodeBuilder(leaf.getQName(), leaf.getPath(), line);
754         convertDataSchemaNode(leaf, builder);
755         builder.setConfiguration(leaf.isConfiguration());
756         final TypeDefinition<?> type = leaf.getType();
757         builder.setType(type);
758         builder.setPath(leaf.getPath());
759         builder.setUnknownNodes(leaf.getUnknownSchemaNodes());
760         builder.setDefaultStr(leaf.getDefault());
761         builder.setUnits(leaf.getUnits());
762         return builder;
763     }
764
765     public static ContainerSchemaNodeBuilder createContainer(ContainerSchemaNode container, int line) {
766         final ContainerSchemaNodeBuilder builder = new ContainerSchemaNodeBuilder(line, container.getQName(),
767                 container.getPath());
768         convertDataSchemaNode(container, builder);
769         builder.setConfiguration(container.isConfiguration());
770         builder.setUnknownNodes(container.getUnknownSchemaNodes());
771         builder.setChildNodes(container.getChildNodes());
772         builder.setGroupings(container.getGroupings());
773         builder.setTypedefs(container.getTypeDefinitions());
774         builder.setAugmentations(container.getAvailableAugmentations());
775         builder.setUsesnodes(container.getUses());
776         builder.setPresence(container.isPresenceContainer());
777         return builder;
778     }
779
780     public static ListSchemaNodeBuilder createList(ListSchemaNode list, int line) {
781         ListSchemaNodeBuilder builder = new ListSchemaNodeBuilder(line, list.getQName(), list.getPath());
782         convertDataSchemaNode(list, builder);
783         builder.setConfiguration(list.isConfiguration());
784         builder.setUnknownNodes(list.getUnknownSchemaNodes());
785         builder.setTypedefs(list.getTypeDefinitions());
786         builder.setChildNodes(list.getChildNodes());
787         builder.setGroupings(list.getGroupings());
788         builder.setAugmentations(list.getAvailableAugmentations());
789         builder.setUsesnodes(list.getUses());
790         builder.setUserOrdered(builder.isUserOrdered());
791         return builder;
792     }
793
794     public static LeafListSchemaNodeBuilder createLeafList(LeafListSchemaNode leafList, int line) {
795         final LeafListSchemaNodeBuilder builder = new LeafListSchemaNodeBuilder(line, leafList.getQName(),
796                 leafList.getPath());
797         convertDataSchemaNode(leafList, builder);
798         builder.setConfiguration(leafList.isConfiguration());
799         builder.setType(leafList.getType());
800         builder.setUnknownNodes(leafList.getUnknownSchemaNodes());
801         builder.setUserOrdered(leafList.isUserOrdered());
802         return builder;
803     }
804
805     public static ChoiceBuilder createChoice(ChoiceNode choice, int line) {
806         final ChoiceBuilder builder = new ChoiceBuilder(line, choice.getQName());
807         convertDataSchemaNode(choice, builder);
808         builder.setConfiguration(choice.isConfiguration());
809         builder.setCases(choice.getCases());
810         builder.setUnknownNodes(choice.getUnknownSchemaNodes());
811         builder.setDefaultCase(choice.getDefaultCase());
812         return builder;
813     }
814
815     public static AnyXmlBuilder createAnyXml(AnyXmlSchemaNode anyxml, int line) {
816         final AnyXmlBuilder builder = new AnyXmlBuilder(line, anyxml.getQName(), anyxml.getPath());
817         convertDataSchemaNode(anyxml, builder);
818         builder.setConfiguration(anyxml.isConfiguration());
819         builder.setUnknownNodes(anyxml.getUnknownSchemaNodes());
820         return builder;
821     }
822
823     public static GroupingBuilder createGrouping(GroupingDefinition grouping, int line) {
824         final GroupingBuilderImpl builder = new GroupingBuilderImpl(grouping.getQName(), line);
825         builder.setPath(grouping.getPath());
826         builder.setChildNodes(grouping.getChildNodes());
827         builder.setGroupings(grouping.getGroupings());
828         builder.setTypedefs(grouping.getTypeDefinitions());
829         builder.setUsesnodes(grouping.getUses());
830         builder.setUnknownNodes(grouping.getUnknownSchemaNodes());
831         builder.setDescription(grouping.getDescription());
832         builder.setReference(grouping.getReference());
833         builder.setStatus(grouping.getStatus());
834         return builder;
835     }
836
837     public static TypeDefinitionBuilder createTypedef(ExtendedType typedef, int line) {
838         final TypeDefinitionBuilderImpl builder = new TypeDefinitionBuilderImpl(typedef.getQName(), line);
839         builder.setPath(typedef.getPath());
840         builder.setDefaultValue(typedef.getDefaultValue());
841         builder.setUnits(typedef.getUnits());
842         builder.setDescription(typedef.getDescription());
843         builder.setReference(typedef.getReference());
844         builder.setStatus(typedef.getStatus());
845         builder.setRanges(typedef.getRanges());
846         builder.setLengths(typedef.getLengths());
847         builder.setPatterns(typedef.getPatterns());
848         builder.setFractionDigits(typedef.getFractionDigits());
849         final TypeDefinition<?> type = typedef.getBaseType();
850         builder.setType(type);
851         builder.setUnits(typedef.getUnits());
852         builder.setUnknownNodes(typedef.getUnknownSchemaNodes());
853         return builder;
854     }
855
856     public static UnknownSchemaNodeBuilder createUnknownSchemaNode(UnknownSchemaNode unknownNode, int line) {
857         final UnknownSchemaNodeBuilder builder = new UnknownSchemaNodeBuilder(line, unknownNode.getQName());
858         builder.setPath(unknownNode.getPath());
859         builder.setUnknownNodes(unknownNode.getUnknownSchemaNodes());
860         builder.setDescription(unknownNode.getDescription());
861         builder.setReference(unknownNode.getReference());
862         builder.setStatus(unknownNode.getStatus());
863         builder.setAddedByUses(unknownNode.isAddedByUses());
864         builder.setNodeType(unknownNode.getNodeType());
865         builder.setNodeParameter(unknownNode.getNodeParameter());
866         return builder;
867     }
868
869     /**
870      * Set DataSchemaNode arguments to builder object
871      *
872      * @param node
873      *            node from which arguments should be read
874      * @param builder
875      *            builder to which arguments should be set
876      */
877     private static void convertDataSchemaNode(DataSchemaNode node, DataSchemaNodeBuilder builder) {
878         builder.setPath(node.getPath());
879         builder.setDescription(node.getDescription());
880         builder.setReference(node.getReference());
881         builder.setStatus(node.getStatus());
882         builder.setAugmenting(node.isAugmenting());
883         copyConstraintsFromDefinition(node.getConstraints(), builder.getConstraints());
884     }
885
886     /**
887      * Copy constraints from constraints definition to constraints builder.
888      *
889      * @param nodeConstraints
890      *            definition from which constraints will be copied
891      * @param constraints
892      *            builder to which constraints will be added
893      */
894     private static void copyConstraintsFromDefinition(final ConstraintDefinition nodeConstraints,
895             final ConstraintsBuilder constraints) {
896         final RevisionAwareXPath when = nodeConstraints.getWhenCondition();
897         final Set<MustDefinition> must = nodeConstraints.getMustConstraints();
898
899         if (when != null) {
900             constraints.addWhenCondition(when.toString());
901         }
902         if (must != null) {
903             for (MustDefinition md : must) {
904                 constraints.addMustDefinition(md);
905             }
906         }
907         constraints.setMandatory(nodeConstraints.isMandatory());
908         constraints.setMinElements(nodeConstraints.getMinElements());
909         constraints.setMaxElements(nodeConstraints.getMaxElements());
910     }
911
912     public static void processAugmentationOnContext(final AugmentationSchemaBuilder augmentBuilder,
913             final List<QName> path, final ModuleBuilder module, final String prefix, final int line,
914             final SchemaContext context) {
915         final Module dependentModule = findModuleFromContext(context, module, prefix, line);
916         if (dependentModule == null) {
917             throw new YangParseException(module.getName(), line, "Failed to find referenced module with prefix "
918                     + prefix + ".");
919         }
920         SchemaNode node = dependentModule.getDataChildByName(path.get(0).getLocalName());
921         if (node == null) {
922             Set<NotificationDefinition> notifications = dependentModule.getNotifications();
923             for (NotificationDefinition ntf : notifications) {
924                 if (ntf.getQName().getLocalName().equals(path.get(0).getLocalName())) {
925                     node = ntf;
926                     break;
927                 }
928             }
929         }
930         if (node == null) {
931             return;
932         }
933
934         for (int i = 1; i < path.size(); i++) {
935             if (node instanceof DataNodeContainer) {
936                 DataNodeContainer ref = (DataNodeContainer) node;
937                 node = ref.getDataChildByName(path.get(i).getLocalName());
938             }
939         }
940         if (node == null) {
941             return;
942         }
943
944         if (node instanceof ContainerSchemaNodeImpl) {
945             // includes container, input and output statement
946             ContainerSchemaNodeImpl c = (ContainerSchemaNodeImpl) node;
947             ContainerSchemaNodeBuilder cb = c.toBuilder();
948             fillAugmentTarget(augmentBuilder, cb);
949             ((AugmentationTargetBuilder) cb).addAugmentation(augmentBuilder);
950             SchemaPath oldPath = cb.getPath();
951             cb.rebuild();
952             augmentBuilder.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
953             augmentBuilder.setResolved(true);
954             module.augmentResolved();
955         } else if (node instanceof ListSchemaNodeImpl) {
956             ListSchemaNodeImpl l = (ListSchemaNodeImpl) node;
957             ListSchemaNodeBuilder lb = l.toBuilder();
958             fillAugmentTarget(augmentBuilder, lb);
959             ((AugmentationTargetBuilder) lb).addAugmentation(augmentBuilder);
960             SchemaPath oldPath = lb.getPath();
961             lb.rebuild();
962             augmentBuilder.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
963             augmentBuilder.setResolved(true);
964             module.augmentResolved();
965         } else if (node instanceof ChoiceNodeImpl) {
966             ChoiceNodeImpl ch = (ChoiceNodeImpl) node;
967             ChoiceBuilder chb = ch.toBuilder();
968             fillAugmentTarget(augmentBuilder, chb);
969             ((AugmentationTargetBuilder) chb).addAugmentation(augmentBuilder);
970             SchemaPath oldPath = chb.getPath();
971             chb.rebuild();
972             augmentBuilder.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
973             augmentBuilder.setResolved(true);
974             module.augmentResolved();
975         } else if (node instanceof ChoiceCaseNodeImpl) {
976             ChoiceCaseNodeImpl chc = (ChoiceCaseNodeImpl) node;
977             ChoiceCaseBuilder chcb = chc.toBuilder();
978             fillAugmentTarget(augmentBuilder, chcb);
979             ((AugmentationTargetBuilder) chcb).addAugmentation(augmentBuilder);
980             SchemaPath oldPath = chcb.getPath();
981             chcb.rebuild();
982             augmentBuilder.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
983             augmentBuilder.setResolved(true);
984             module.augmentResolved();
985         } else if (node instanceof NotificationDefinitionImpl) {
986             NotificationDefinitionImpl nd = (NotificationDefinitionImpl) node;
987             NotificationBuilder nb = nd.toBuilder();
988             fillAugmentTarget(augmentBuilder, nb);
989             ((AugmentationTargetBuilder) nb).addAugmentation(augmentBuilder);
990             SchemaPath oldPath = nb.getPath();
991             nb.rebuild();
992             augmentBuilder.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
993             augmentBuilder.setResolved(true);
994             module.augmentResolved();
995         } else {
996             throw new YangParseException(module.getName(), line, "Target of type " + node.getClass()
997                     + " can not be augmented.");
998         }
999     }
1000
1001     public static void processAugmentation(final AugmentationSchemaBuilder augmentBuilder, final List<QName> path,
1002             final ModuleBuilder module, final ModuleBuilder dependentModuleBuilder) {
1003         DataSchemaNodeBuilder currentParent = null;
1004         for (DataSchemaNodeBuilder child : dependentModuleBuilder.getChildNodeBuilders()) {
1005             final QName childQName = child.getQName();
1006             if (childQName.getLocalName().equals(path.get(0).getLocalName())) {
1007                 currentParent = child;
1008                 break;
1009             }
1010         }
1011
1012         if (currentParent == null) {
1013             return;
1014         }
1015
1016         for (int i = 1; i < path.size(); i++) {
1017             final QName currentQName = path.get(i);
1018             DataSchemaNodeBuilder newParent = null;
1019             if (currentParent instanceof DataNodeContainerBuilder) {
1020                 for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) currentParent).getChildNodeBuilders()) {
1021                     final QName childQName = child.getQName();
1022                     if (childQName.getLocalName().equals(currentQName.getLocalName())) {
1023                         newParent = child;
1024                         break;
1025                     }
1026                 }
1027             } else if (currentParent instanceof ChoiceBuilder) {
1028                 for (ChoiceCaseBuilder caseBuilder : ((ChoiceBuilder) currentParent).getCases()) {
1029                     final QName caseQName = caseBuilder.getQName();
1030                     if (caseQName.getLocalName().equals(currentQName.getLocalName())) {
1031                         newParent = caseBuilder;
1032                         break;
1033                     }
1034                 }
1035             }
1036
1037             if (newParent == null) {
1038                 break; // node not found, quit search
1039             } else {
1040                 currentParent = newParent;
1041             }
1042         }
1043
1044         final String currentName = currentParent.getQName().getLocalName();
1045         final String lastAugmentPathElementName = path.get(path.size() - 1).getLocalName();
1046         if (currentName.equals(lastAugmentPathElementName)) {
1047
1048             if (currentParent instanceof ChoiceBuilder) {
1049                 fillAugmentTarget(augmentBuilder, (ChoiceBuilder) currentParent);
1050             } else {
1051                 fillAugmentTarget(augmentBuilder, (DataNodeContainerBuilder) currentParent);
1052             }
1053             ((AugmentationTargetBuilder) currentParent).addAugmentation(augmentBuilder);
1054             SchemaPath oldPath = currentParent.getPath();
1055             augmentBuilder.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
1056             augmentBuilder.setResolved(true);
1057             module.augmentResolved();
1058         }
1059     }
1060
1061     /**
1062      * Create new type builder based on old type with new base type.
1063      *
1064      * @param newBaseType
1065      *            new base type builder
1066      * @param oldExtendedType
1067      *            old type
1068      * @param modules
1069      *            all loaded modules
1070      * @param module
1071      *            current module
1072      * @param line
1073      *            current line in module
1074      * @return new type builder based on old type with new base type
1075      */
1076     public static TypeDefinitionBuilder extendedTypeWithNewBaseTypeBuilder(final TypeDefinitionBuilder newBaseType,
1077             final ExtendedType oldExtendedType, final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1078             final ModuleBuilder module, final int line) {
1079         final TypeConstraints tc = new TypeConstraints(module.getName(), line);
1080         tc.addFractionDigits(oldExtendedType.getFractionDigits());
1081         tc.addLengths(oldExtendedType.getLengths());
1082         tc.addPatterns(oldExtendedType.getPatterns());
1083         tc.addRanges(oldExtendedType.getRanges());
1084
1085         final TypeConstraints constraints = findConstraintsFromTypeBuilder(newBaseType, tc, modules, module, null);
1086         final TypeDefinitionBuilderImpl newType = new TypeDefinitionBuilderImpl(oldExtendedType.getQName(), line);
1087         newType.setTypedef(newBaseType);
1088         newType.setPath(oldExtendedType.getPath());
1089         newType.setDescription(oldExtendedType.getDescription());
1090         newType.setReference(oldExtendedType.getReference());
1091         newType.setStatus(oldExtendedType.getStatus());
1092         newType.setLengths(constraints.getLength());
1093         newType.setPatterns(constraints.getPatterns());
1094         newType.setRanges(constraints.getRange());
1095         newType.setFractionDigits(constraints.getFractionDigits());
1096         newType.setUnits(oldExtendedType.getUnits());
1097         newType.setDefaultValue(oldExtendedType.getDefaultValue());
1098         newType.setUnknownNodes(oldExtendedType.getUnknownSchemaNodes());
1099         return newType;
1100     }
1101
1102     /**
1103      * Create new type builder based on old type with new base type.
1104      *
1105      * @param newBaseType
1106      *            new base type
1107      * @param oldExtendedType
1108      *            old type
1109      * @param modules
1110      *            all loaded modules
1111      * @param module
1112      *            current module
1113      * @param line
1114      *            current line in module
1115      * @return new type builder based on old type with new base type
1116      */
1117     public static TypeDefinitionBuilder extendedTypeWithNewBaseType(final TypeDefinition<?> newBaseType,
1118             final ExtendedType oldExtendedType, final ModuleBuilder module, final int line) {
1119         final TypeConstraints tc = new TypeConstraints(module.getName(), line);
1120
1121         final TypeConstraints constraints = findConstraintsFromTypeDefinition(newBaseType, tc);
1122         final TypeDefinitionBuilderImpl newType = new TypeDefinitionBuilderImpl(oldExtendedType.getQName(), line);
1123         newType.setType(newBaseType);
1124         newType.setPath(oldExtendedType.getPath());
1125         newType.setDescription(oldExtendedType.getDescription());
1126         newType.setReference(oldExtendedType.getReference());
1127         newType.setStatus(oldExtendedType.getStatus());
1128         newType.setLengths(constraints.getLength());
1129         newType.setPatterns(constraints.getPatterns());
1130         newType.setRanges(constraints.getRange());
1131         newType.setFractionDigits(constraints.getFractionDigits());
1132         newType.setUnits(oldExtendedType.getUnits());
1133         newType.setDefaultValue(oldExtendedType.getDefaultValue());
1134         newType.setUnknownNodes(oldExtendedType.getUnknownSchemaNodes());
1135         return newType;
1136     }
1137
1138     /**
1139      * Pull restrictions from type and add them to constraints.
1140      *
1141      * @param typeToResolve
1142      *            type from which constraints will be read
1143      * @param constraints
1144      *            constraints object to which constraints will be added
1145      * @return constraints contstraints object containing constraints from given
1146      *         type
1147      */
1148     private static TypeConstraints findConstraintsFromTypeDefinition(final TypeDefinition<?> typeToResolve,
1149             final TypeConstraints constraints) {
1150         // union type cannot be restricted
1151         if (typeToResolve instanceof UnionTypeDefinition) {
1152             return constraints;
1153         }
1154         if (typeToResolve instanceof ExtendedType) {
1155             ExtendedType extType = (ExtendedType) typeToResolve;
1156             constraints.addFractionDigits(extType.getFractionDigits());
1157             constraints.addLengths(extType.getLengths());
1158             constraints.addPatterns(extType.getPatterns());
1159             constraints.addRanges(extType.getRanges());
1160             return findConstraintsFromTypeDefinition(extType.getBaseType(), constraints);
1161         } else {
1162             mergeConstraints(typeToResolve, constraints);
1163             return constraints;
1164         }
1165     }
1166
1167     public static TypeConstraints findConstraintsFromTypeBuilder(final TypeAwareBuilder nodeToResolve,
1168             final TypeConstraints constraints, final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1169             final ModuleBuilder builder, final SchemaContext context) {
1170
1171         // union type cannot be restricted
1172         if (nodeToResolve instanceof UnionTypeBuilder) {
1173             return constraints;
1174         }
1175
1176         if (nodeToResolve instanceof TypeDefinitionBuilder) {
1177             TypeDefinitionBuilder typedefToResolve = (TypeDefinitionBuilder) nodeToResolve;
1178             constraints.addFractionDigits(typedefToResolve.getFractionDigits());
1179             constraints.addLengths(typedefToResolve.getLengths());
1180             constraints.addPatterns(typedefToResolve.getPatterns());
1181             constraints.addRanges(typedefToResolve.getRanges());
1182         }
1183
1184         TypeDefinition<?> type = nodeToResolve.getType();
1185         if (type == null) {
1186             return findConstraintsFromTypeBuilder(nodeToResolve.getTypedef(), constraints, modules, builder, context);
1187         } else {
1188             QName qname = type.getQName();
1189             if (type instanceof UnknownType) {
1190                 ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, builder, qname.getPrefix(),
1191                         nodeToResolve.getLine());
1192                 if (dependentModuleBuilder == null) {
1193                     if (context == null) {
1194                         throw new YangParseException(builder.getName(), nodeToResolve.getLine(),
1195                                 "Failed to resolved type constraints.");
1196                     }
1197                     Module dm = findModuleFromContext(context, builder, qname.getPrefix(), nodeToResolve.getLine());
1198                     TypeDefinition<?> t = findTypeByName(dm.getTypeDefinitions(), qname.getLocalName());
1199                     if (t instanceof ExtendedType) {
1200                         ExtendedType extType = (ExtendedType) t;
1201                         constraints.addFractionDigits(extType.getFractionDigits());
1202                         constraints.addLengths(extType.getLengths());
1203                         constraints.addPatterns(extType.getPatterns());
1204                         constraints.addRanges(extType.getRanges());
1205                         return constraints;
1206                     } else {
1207                         mergeConstraints(t, constraints);
1208                         return constraints;
1209                     }
1210                 } else {
1211                     TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(nodeToResolve, dependentModuleBuilder,
1212                             qname.getLocalName(), builder.getName(), nodeToResolve.getLine());
1213                     return findConstraintsFromTypeBuilder(tdb, constraints, modules, dependentModuleBuilder, context);
1214                 }
1215             } else if (type instanceof ExtendedType) {
1216                 ExtendedType extType = (ExtendedType) type;
1217                 constraints.addFractionDigits(extType.getFractionDigits());
1218                 constraints.addLengths(extType.getLengths());
1219                 constraints.addPatterns(extType.getPatterns());
1220                 constraints.addRanges(extType.getRanges());
1221
1222                 TypeDefinition<?> base = extType.getBaseType();
1223                 if (base instanceof UnknownType) {
1224                     ModuleBuilder dependentModule = findDependentModuleBuilder(modules, builder, base.getQName()
1225                             .getPrefix(), nodeToResolve.getLine());
1226                     TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(nodeToResolve, dependentModule, base
1227                             .getQName().getLocalName(), builder.getName(), nodeToResolve.getLine());
1228                     return findConstraintsFromTypeBuilder(tdb, constraints, modules, dependentModule, context);
1229                 } else {
1230                     // it has to be base yang type
1231                     mergeConstraints(type, constraints);
1232                     return constraints;
1233                 }
1234             } else {
1235                 // it is base yang type
1236                 mergeConstraints(type, constraints);
1237                 return constraints;
1238             }
1239         }
1240     }
1241
1242     /**
1243      * Search for type definition builder by name.
1244      *
1245      * @param dirtyNodeSchemaPath
1246      *            schema path of node which contains unresolved type
1247      * @param dependentModule
1248      *            module which should contains referenced type
1249      * @param typeName
1250      *            name of type definition
1251      * @param currentModuleName
1252      *            name of current module
1253      * @param line
1254      *            current line in yang model
1255      * @return
1256      */
1257     public static TypeDefinitionBuilder findTypeDefinitionBuilder(final TypeAwareBuilder nodeToResolve,
1258             final ModuleBuilder dependentModule, final String typeName, final String currentModuleName, final int line) {
1259
1260         TypeDefinitionBuilder result = null;
1261
1262         Set<TypeDefinitionBuilder> typedefs = dependentModule.getTypeDefinitionBuilders();
1263         result = findTypedefBuilderByName(typedefs, typeName);
1264         if (result != null) {
1265             return result;
1266         }
1267
1268         Builder parent = nodeToResolve.getParent();
1269         while (parent != null) {
1270             if (parent instanceof DataNodeContainerBuilder) {
1271                 typedefs = ((DataNodeContainerBuilder) parent).getTypeDefinitionBuilders();
1272             } else if (parent instanceof RpcDefinitionBuilder) {
1273                 typedefs = ((RpcDefinitionBuilder) parent).getTypeDefinitions();
1274             }
1275             result = findTypedefBuilderByName(typedefs, typeName);
1276             if (result == null) {
1277                 parent = parent.getParent();
1278             } else {
1279                 break;
1280             }
1281         }
1282
1283         if (result == null) {
1284             throw new YangParseException(currentModuleName, line, "Referenced type '" + typeName + "' not found.");
1285         }
1286         return result;
1287     }
1288
1289 }