Merge "Bug 1027: Decreased log level from INFO to DEBUG."
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / builder / impl / ModuleBuilder.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
3  * This program and the accompanying materials are made available under the
4  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/epl-v10.html
6  */
7 package org.opendaylight.yangtools.yang.parser.builder.impl;
8
9 import com.google.common.io.ByteSource;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.URI;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Date;
17 import java.util.Deque;
18 import java.util.HashSet;
19 import java.util.LinkedHashSet;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.TreeSet;
24
25 import org.apache.commons.io.IOUtils;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
28 import org.opendaylight.yangtools.yang.model.api.Deviation;
29 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
30 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
31 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.Module;
33 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
34 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
35 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
36 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
37 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
38 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
39 import org.opendaylight.yangtools.yang.model.util.ModuleImportImpl;
40 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
41 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
42 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
43 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
44 import org.opendaylight.yangtools.yang.parser.builder.api.DocumentedNodeBuilder;
45 import org.opendaylight.yangtools.yang.parser.builder.api.ExtensionBuilder;
46 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
47 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
48 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
49 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
50 import org.opendaylight.yangtools.yang.parser.builder.api.UnknownSchemaNodeBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
52 import org.opendaylight.yangtools.yang.parser.builder.util.AbstractDocumentedDataNodeContainerBuilder;
53 import org.opendaylight.yangtools.yang.parser.builder.util.Comparators;
54 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
55
56 /**
57  * Builder of Module object. If this module is dependent on external
58  * module/modules, these dependencies must be resolved before module is built,
59  * otherwise result may not be valid.
60  */
61 public class ModuleBuilder extends AbstractDocumentedDataNodeContainerBuilder implements DocumentedNodeBuilder {
62
63     private ModuleImpl instance;
64     private final String name;
65     private final String sourcePath;
66     private static final SchemaPath SCHEMA_PATH = SchemaPath.create(Collections.<QName> emptyList(), true);
67     private URI namespace;
68     private String prefix;
69     private Date revision;
70
71     private final boolean submodule;
72     private String belongsTo;
73     private ModuleBuilder parent;
74
75     private final Deque<Builder> actualPath = new LinkedList<>();
76     private final Set<TypeAwareBuilder> dirtyNodes = new HashSet<>();
77
78     final Set<ModuleImport> imports = new HashSet<ModuleImport>();
79
80     private final Set<AugmentationSchema> augments = new LinkedHashSet<>();
81     private final List<AugmentationSchemaBuilder> augmentBuilders = new ArrayList<>();
82     private final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
83
84     private final List<GroupingBuilder> allGroupings = new ArrayList<>();
85
86     private final List<UsesNodeBuilder> allUsesNodes = new ArrayList<>();
87
88     private final Set<RpcDefinition> rpcs = new TreeSet<>(Comparators.SCHEMA_NODE_COMP);
89     private final Set<RpcDefinitionBuilder> addedRpcs = new HashSet<>();
90
91     private final Set<NotificationDefinition> notifications = new TreeSet<>(Comparators.SCHEMA_NODE_COMP);
92     private final Set<NotificationBuilder> addedNotifications = new HashSet<>();
93
94     private final Set<IdentitySchemaNode> identities = new TreeSet<>(Comparators.SCHEMA_NODE_COMP);
95     private final Set<IdentitySchemaNodeBuilder> addedIdentities = new HashSet<>();
96
97     private final Set<FeatureDefinition> features = new TreeSet<>(Comparators.SCHEMA_NODE_COMP);
98     private final Set<FeatureBuilder> addedFeatures = new HashSet<>();
99
100     private final Set<Deviation> deviations = new HashSet<>();
101     private final Set<DeviationBuilder> deviationBuilders = new HashSet<>();
102
103     private final List<ExtensionDefinition> extensions = new ArrayList<>();
104     private final List<ExtensionBuilder> addedExtensions = new ArrayList<>();
105
106     private final List<UnknownSchemaNodeBuilder> allUnknownNodes = new ArrayList<>();
107
108     private final List<ListSchemaNodeBuilder> allLists = new ArrayList<>();
109
110     private String source;
111     private String yangVersion;
112     private String organization;
113     private String contact;
114
115     public ModuleBuilder(final String name, final String sourcePath) {
116         this(name, false, sourcePath);
117     }
118
119     public ModuleBuilder(final String name, final boolean submodule, final String sourcePath) {
120         super(name, 0, null);
121         this.name = name;
122         this.sourcePath = sourcePath;
123         this.submodule = submodule;
124         actualPath.push(this);//FIXME: this escapes constructor
125     }
126
127     public ModuleBuilder(final Module base) {
128         super(base.getName(), 0, new QName(base.getNamespace(), base.getRevision(), base.getPrefix(), base.getName()),
129                 SCHEMA_PATH, base);
130         this.name = base.getName();
131         this.sourcePath = base.getModuleSourcePath();
132
133         submodule = false;
134         yangVersion = base.getYangVersion();
135         actualPath.push(this);//FIXME: this escapes constructor
136         namespace = base.getNamespace();
137         prefix = base.getPrefix();
138         revision = base.getRevision();
139
140         augments.addAll(base.getAugmentations());
141         rpcs.addAll(base.getRpcs());
142         notifications.addAll(base.getNotifications());
143
144         for (IdentitySchemaNode identityNode : base.getIdentities()) {
145             addedIdentities.add(new IdentitySchemaNodeBuilder(name, identityNode));
146         }
147
148         features.addAll(base.getFeatures());
149         deviations.addAll(base.getDeviations());
150         extensions.addAll(base.getExtensionSchemaNodes());
151         unknownNodes.addAll(base.getUnknownSchemaNodes());
152         source = base.getSource();
153     }
154
155     @Override
156     protected String getStatementName() {
157         return "module";
158     }
159
160     /**
161      * Build new Module object based on this builder.
162      */
163     @Override
164     public Module build() {
165         if(instance != null) {
166             return instance;
167         }
168
169         buildChildren();
170
171         // FEATURES
172         for (FeatureBuilder fb : addedFeatures) {
173             features.add(fb.build());
174         }
175
176         // NOTIFICATIONS
177         for (NotificationBuilder entry : addedNotifications) {
178             notifications.add(entry.build());
179         }
180
181         // AUGMENTATIONS
182         for (AugmentationSchemaBuilder builder : augmentBuilders) {
183             augments.add(builder.build());
184         }
185
186         // RPCs
187         for (RpcDefinitionBuilder rpc : addedRpcs) {
188             rpcs.add(rpc.build());
189         }
190
191         // DEVIATIONS
192         for (DeviationBuilder entry : deviationBuilders) {
193             deviations.add(entry.build());
194         }
195
196         // EXTENSIONS
197         for (ExtensionBuilder eb : addedExtensions) {
198             extensions.add(eb.build());
199         }
200         Collections.sort(extensions, Comparators.SCHEMA_NODE_COMP);
201
202
203         // IDENTITIES
204         for (IdentitySchemaNodeBuilder id : addedIdentities) {
205             identities.add(id.build());
206         }
207
208         // UNKNOWN NODES
209         for (UnknownSchemaNodeBuilder unb : addedUnknownNodes) {
210             unknownNodes.add(unb.build());
211         }
212         Collections.sort(unknownNodes, Comparators.SCHEMA_NODE_COMP);
213
214         instance = new ModuleImpl(name, sourcePath, this);
215         return instance;
216     }
217
218     public String getModuleSourcePath() {
219         return sourcePath;
220     }
221
222     @Override
223     public ModuleBuilder getParent() {
224         return parent;
225     }
226
227     public void setParent(final ModuleBuilder parent) {
228         this.parent = parent;
229     }
230
231     @Override
232     public void setParent(final Builder parent) {
233         throw new YangParseException(name, 0, "Can not set parent to module");
234     }
235
236     @Override
237     public SchemaPath getPath() {
238         return SCHEMA_PATH;
239     }
240
241     public void enterNode(final Builder node) {
242         actualPath.push(node);
243     }
244
245     public void exitNode() {
246         actualPath.pop();
247     }
248
249     public Builder getActualNode() {
250         if (actualPath.isEmpty()) {
251             return null;
252         } else {
253             return actualPath.peekFirst();
254         }
255     }
256
257     public Set<TypeAwareBuilder> getDirtyNodes() {
258         return dirtyNodes;
259     }
260
261     public Set<AugmentationSchema> getAugments() {
262         return augments;
263     }
264
265     public List<AugmentationSchemaBuilder> getAugmentBuilders() {
266         return augmentBuilders;
267     }
268
269     public List<AugmentationSchemaBuilder> getAllAugments() {
270         return allAugments;
271     }
272
273     public Set<IdentitySchemaNode> getIdentities() {
274         return identities;
275     }
276
277     public Set<IdentitySchemaNodeBuilder> getAddedIdentities() {
278         return addedIdentities;
279     }
280
281     public Set<FeatureDefinition> getFeatures() {
282         return features;
283     }
284
285     public Set<FeatureBuilder> getAddedFeatures() {
286         return addedFeatures;
287     }
288
289     public List<GroupingBuilder> getAllGroupings() {
290         return allGroupings;
291     }
292
293     public List<UsesNodeBuilder> getAllUsesNodes() {
294         return allUsesNodes;
295     }
296
297     public Set<Deviation> getDeviations() {
298         return deviations;
299     }
300
301     public Set<DeviationBuilder> getDeviationBuilders() {
302         return deviationBuilders;
303     }
304
305     public List<ExtensionDefinition> getExtensions() {
306         return extensions;
307     }
308
309     public List<ExtensionBuilder> getAddedExtensions() {
310         return addedExtensions;
311     }
312
313     public List<UnknownSchemaNodeBuilder> getAllUnknownNodes() {
314         return allUnknownNodes;
315     }
316
317     public List<ListSchemaNodeBuilder> getAllLists() {
318         return allLists;
319     }
320
321     public String getName() {
322         return name;
323     }
324
325     public URI getNamespace() {
326         return namespace;
327     }
328
329     public void setNamespace(final URI namespace) {
330         this.namespace = namespace;
331     }
332
333     public String getPrefix() {
334         return prefix;
335     }
336
337     public Date getRevision() {
338         return revision;
339     }
340
341     protected Set<ModuleImport> getImports() {
342         return imports;
343     }
344
345     protected String getSource() {
346         return source;
347     }
348
349     public boolean isSubmodule() {
350         return submodule;
351     }
352
353     public String getBelongsTo() {
354         return belongsTo;
355     }
356
357     public void setBelongsTo(final String belongsTo) {
358         this.belongsTo = belongsTo;
359     }
360
361     public void markActualNodeDirty() {
362         final TypeAwareBuilder nodeBuilder = (TypeAwareBuilder) getActualNode();
363         dirtyNodes.add(nodeBuilder);
364     }
365
366     public void setRevision(final Date revision) {
367         this.revision = revision;
368     }
369
370     public void setPrefix(final String prefix) {
371         this.prefix = prefix;
372     }
373
374     public void setYangVersion(final String yangVersion) {
375         this.yangVersion = yangVersion;
376     }
377
378     public void setOrganization(final String organization) {
379         this.organization = organization;
380     }
381
382     public void setContact(final String contact) {
383         this.contact = contact;
384     }
385
386     public boolean addModuleImport(final String moduleName, final Date revision, final String prefix) {
387         checkNotSealed();
388         final ModuleImport moduleImport = createModuleImport(moduleName, revision, prefix);
389         return imports.add(moduleImport);
390     }
391
392     public Set<ModuleImport> getModuleImports() {
393         return imports;
394     }
395
396     public ExtensionBuilder addExtension(final QName qname, final int line, final SchemaPath path) {
397         checkNotSealed();
398         Builder parent = getActualNode();
399         if (!(parent.equals(this))) {
400             throw new YangParseException(name, line, "extension can be defined only in module or submodule");
401         }
402
403         final String extName = qname.getLocalName();
404         for (ExtensionBuilder addedExtension : addedExtensions) {
405             if (addedExtension.getQName().getLocalName().equals(extName)) {
406                 raiseYangParserException("extension", "node", extName, line, addedExtension.getLine());
407             }
408         }
409         final ExtensionBuilder builder = new ExtensionBuilderImpl(name, line, qname, path);
410         builder.setParent(parent);
411         addedExtensions.add(builder);
412         return builder;
413     }
414
415     public ContainerSchemaNodeBuilder addContainerNode(final int line, final QName qname, final SchemaPath schemaPath) {
416         checkNotSealed();
417         final ContainerSchemaNodeBuilder builder = new ContainerSchemaNodeBuilder(name, line, qname, schemaPath);
418
419         Builder parent = getActualNode();
420         builder.setParent(parent);
421         addChildToParent(parent, builder, qname.getLocalName());
422
423         return builder;
424     }
425
426     public ListSchemaNodeBuilder addListNode(final int line, final QName qname, final SchemaPath schemaPath) {
427         checkNotSealed();
428         final ListSchemaNodeBuilder builder = new ListSchemaNodeBuilder(name, line, qname, schemaPath);
429
430         Builder parent = getActualNode();
431         builder.setParent(parent);
432         addChildToParent(parent, builder, qname.getLocalName());
433         allLists.add(builder);
434
435         return builder;
436     }
437
438     public LeafSchemaNodeBuilder addLeafNode(final int line, final QName qname, final SchemaPath schemaPath) {
439         checkNotSealed();
440         final LeafSchemaNodeBuilder builder = new LeafSchemaNodeBuilder(name, line, qname, schemaPath);
441
442         Builder parent = getActualNode();
443         builder.setParent(parent);
444         addChildToParent(parent, builder, qname.getLocalName());
445
446         return builder;
447     }
448
449     public LeafListSchemaNodeBuilder addLeafListNode(final int line, final QName qname, final SchemaPath schemaPath) {
450         checkNotSealed();
451         final LeafListSchemaNodeBuilder builder = new LeafListSchemaNodeBuilder(name, line, qname, schemaPath);
452
453         Builder parent = getActualNode();
454         builder.setParent(parent);
455         addChildToParent(parent, builder, qname.getLocalName());
456
457         return builder;
458     }
459
460     public GroupingBuilder addGrouping(final int line, final QName qname, final SchemaPath path) {
461         checkNotSealed();
462         final GroupingBuilder builder = new GroupingBuilderImpl(name, line, qname, path);
463
464         Builder parent = getActualNode();
465         builder.setParent(parent);
466
467         String groupingName = qname.getLocalName();
468         if (parent.equals(this)) {
469             for (GroupingBuilder addedGrouping : getGroupingBuilders()) {
470                 if (addedGrouping.getQName().getLocalName().equals(groupingName)) {
471                     raiseYangParserException("", "Grouping", groupingName, line, addedGrouping.getLine());
472                 }
473             }
474             addGrouping(builder);
475         } else {
476             if (parent instanceof DataNodeContainerBuilder) {
477                 DataNodeContainerBuilder parentNode = (DataNodeContainerBuilder) parent;
478                 for (GroupingBuilder addedGrouping : parentNode.getGroupingBuilders()) {
479                     if (addedGrouping.getQName().getLocalName().equals(groupingName)) {
480                         raiseYangParserException("", "Grouping", groupingName, line, addedGrouping.getLine());
481                     }
482                 }
483                 parentNode.addGrouping(builder);
484             } else if (parent instanceof RpcDefinitionBuilder) {
485                 RpcDefinitionBuilder parentNode = (RpcDefinitionBuilder) parent;
486                 for (GroupingBuilder child : parentNode.getGroupings()) {
487                     if (child.getQName().getLocalName().equals(groupingName)) {
488                         raiseYangParserException("", "Grouping", groupingName, line, child.getLine());
489                     }
490                 }
491                 parentNode.addGrouping(builder);
492             } else {
493                 throw new YangParseException(name, line, "Unresolved parent of grouping " + groupingName);
494             }
495         }
496
497         allGroupings.add(builder);
498         return builder;
499     }
500
501     public AugmentationSchemaBuilder addAugment(final int line, final String augmentTargetStr, final int order) {
502         checkNotSealed();
503         final AugmentationSchemaBuilder builder = new AugmentationSchemaBuilderImpl(name, line, augmentTargetStr, order);
504
505         Builder parent = getActualNode();
506         builder.setParent(parent);
507
508         if (parent.equals(this)) {
509             // augment can be declared only under 'module' ...
510             if (!(augmentTargetStr.startsWith("/"))) {
511                 throw new YangParseException(
512                         name,
513                         line,
514                         "If the 'augment' statement is on the top level in a module, the absolute form of a schema node identifier MUST be used.");
515             }
516             augmentBuilders.add(builder);
517         } else {
518             // ... or 'uses' statement
519             if (parent instanceof UsesNodeBuilder) {
520                 if (augmentTargetStr.startsWith("/")) {
521                     throw new YangParseException(name, line,
522                             "If 'augment' statement is a substatement to the 'uses' statement, it cannot contain absolute path ("
523                                     + augmentTargetStr + ")");
524                 }
525                 ((UsesNodeBuilder) parent).addAugment(builder);
526             } else {
527                 throw new YangParseException(name, line, "Augment can be declared only under module or uses statement.");
528             }
529         }
530         allAugments.add(builder);
531
532         return builder;
533     }
534
535     public UsesNodeBuilder addUsesNode(final int line, final String groupingPathStr) {
536         checkNotSealed();
537         final UsesNodeBuilder usesBuilder = new UsesNodeBuilderImpl(name, line, groupingPathStr);
538
539         Builder parent = getActualNode();
540         usesBuilder.setParent(parent);
541
542         if (parent.equals(this)) {
543             addUsesNode(usesBuilder);
544         } else {
545             if (!(parent instanceof DataNodeContainerBuilder)) {
546                 throw new YangParseException(name, line, "Unresolved parent of uses '" + groupingPathStr + "'.");
547             }
548             ((DataNodeContainerBuilder) parent).addUsesNode(usesBuilder);
549         }
550         if (parent instanceof AugmentationSchemaBuilder) {
551             usesBuilder.setAugmenting(true);
552         }
553
554         allUsesNodes.add(usesBuilder);
555         return usesBuilder;
556     }
557
558     public void addRefine(final RefineHolderImpl refine) {
559         checkNotSealed();
560         final Builder parent = getActualNode();
561         if (!(parent instanceof UsesNodeBuilder)) {
562             throw new YangParseException(name, refine.getLine(), "refine can be defined only in uses statement");
563         }
564         ((UsesNodeBuilder) parent).addRefine(refine);
565         refine.setParent(parent);
566     }
567
568     public RpcDefinitionBuilder addRpc(final int line, final QName qname, final SchemaPath path) {
569         checkNotSealed();
570         Builder parent = getActualNode();
571         if (!(parent.equals(this))) {
572             throw new YangParseException(name, line, "rpc can be defined only in module or submodule");
573         }
574
575         final RpcDefinitionBuilder rpcBuilder = new RpcDefinitionBuilder(name, line, qname, path);
576         rpcBuilder.setParent(parent);
577
578         String rpcName = qname.getLocalName();
579         checkNotConflictingInDataNamespace(rpcName, line);
580         addedRpcs.add(rpcBuilder);
581         return rpcBuilder;
582     }
583
584     private void checkNotConflictingInDataNamespace(final String rpcName, final int line) {
585         for (DataSchemaNodeBuilder addedChild : getChildNodeBuilders()) {
586             if (addedChild.getQName().getLocalName().equals(rpcName)) {
587                 raiseYangParserException("rpc", "node", rpcName, line, addedChild.getLine());
588             }
589         }
590         for (RpcDefinitionBuilder rpc : addedRpcs) {
591             if (rpc.getQName().getLocalName().equals(rpcName)) {
592                 raiseYangParserException("", "rpc", rpcName, line, rpc.getLine());
593             }
594         }
595         for (NotificationBuilder addedNotification : addedNotifications) {
596             if (addedNotification.getQName().getLocalName().equals(rpcName)) {
597                 raiseYangParserException("rpc", "notification", rpcName, line, addedNotification.getLine());
598             }
599         }
600     }
601
602     public ContainerSchemaNodeBuilder addRpcInput(final int line, final QName qname, final SchemaPath schemaPath) {
603         checkNotSealed();
604         final Builder parent = getActualNode();
605         if (!(parent instanceof RpcDefinitionBuilder)) {
606             throw new YangParseException(name, line, "input can be defined only in rpc statement");
607         }
608         final RpcDefinitionBuilder rpc = (RpcDefinitionBuilder) parent;
609
610         final ContainerSchemaNodeBuilder inputBuilder = new ContainerSchemaNodeBuilder(name, line, qname, schemaPath);
611         inputBuilder.setParent(rpc);
612
613         rpc.setInput(inputBuilder);
614         return inputBuilder;
615     }
616
617     public ContainerSchemaNodeBuilder addRpcOutput(final SchemaPath schemaPath, final QName qname, final int line) {
618         checkNotSealed();
619         final Builder parent = getActualNode();
620         if (!(parent instanceof RpcDefinitionBuilder)) {
621             throw new YangParseException(name, line, "output can be defined only in rpc statement");
622         }
623         final RpcDefinitionBuilder rpc = (RpcDefinitionBuilder) parent;
624
625         final ContainerSchemaNodeBuilder outputBuilder = new ContainerSchemaNodeBuilder(name, line, qname, schemaPath);
626         outputBuilder.setParent(rpc);
627
628         rpc.setOutput(outputBuilder);
629         return outputBuilder;
630     }
631
632     public void addNotification(final NotificationDefinition notification) {
633         checkNotSealed();
634         notifications.add(notification);
635     }
636
637     public NotificationBuilder addNotification(final int line, final QName qname, final SchemaPath path) {
638         checkNotSealed();
639         final Builder parent = getActualNode();
640         if (!(parent.equals(this))) {
641             throw new YangParseException(name, line, "notification can be defined only in module or submodule");
642         }
643
644         String notificationName = qname.getLocalName();
645         checkNotConflictingInDataNamespace(notificationName, line);
646
647         final NotificationBuilder builder = new NotificationBuilder(name, line, qname, path);
648         builder.setParent(parent);
649         addedNotifications.add(builder);
650
651         return builder;
652     }
653
654     public FeatureBuilder addFeature(final int line, final QName qname, final SchemaPath path) {
655         Builder parent = getActualNode();
656         if (!(parent.equals(this))) {
657             throw new YangParseException(name, line, "feature can be defined only in module or submodule");
658         }
659
660         final FeatureBuilder builder = new FeatureBuilder(name, line, qname, path);
661         builder.setParent(parent);
662
663         String featureName = qname.getLocalName();
664         for (FeatureBuilder addedFeature : addedFeatures) {
665             if (addedFeature.getQName().getLocalName().equals(featureName)) {
666                 raiseYangParserException("", "feature", featureName, line, addedFeature.getLine());
667             }
668         }
669         addedFeatures.add(builder);
670         return builder;
671     }
672
673     public ChoiceBuilder addChoice(final int line, final QName qname, final SchemaPath path) {
674         final ChoiceBuilder builder = new ChoiceBuilder(name, line, qname, path);
675
676         Builder parent = getActualNode();
677         builder.setParent(parent);
678         addChildToParent(parent, builder, qname.getLocalName());
679
680         return builder;
681     }
682
683     public ChoiceCaseBuilder addCase(final int line, final QName qname, final SchemaPath path) {
684         Builder parent = getActualNode();
685         if (parent == null || parent.equals(this)) {
686             throw new YangParseException(name, line, "'case' parent not found");
687         }
688
689         final ChoiceCaseBuilder builder = new ChoiceCaseBuilder(name, line, qname, path);
690         builder.setParent(parent);
691
692         if (parent instanceof ChoiceBuilder) {
693             ((ChoiceBuilder) parent).addCase(builder);
694         } else if (parent instanceof AugmentationSchemaBuilder) {
695             ((AugmentationSchemaBuilder) parent).addChildNode(builder);
696         } else {
697             throw new YangParseException(name, line, "Unresolved parent of 'case' " + qname.getLocalName());
698         }
699
700         return builder;
701     }
702
703     public AnyXmlBuilder addAnyXml(final int line, final QName qname, final SchemaPath schemaPath) {
704         final AnyXmlBuilder builder = new AnyXmlBuilder(name, line, qname, schemaPath);
705
706         Builder parent = getActualNode();
707         builder.setParent(parent);
708         addChildToParent(parent, builder, qname.getLocalName());
709
710         return builder;
711     }
712
713     @Override
714     public void addTypedef(final TypeDefinitionBuilder typedefBuilder) {
715         String nodeName = typedefBuilder.getQName().getLocalName();
716         for (TypeDefinitionBuilder tdb : getTypeDefinitionBuilders()) {
717             if (tdb.getQName().getLocalName().equals(nodeName)) {
718                 raiseYangParserException("", "typedef", nodeName, typedefBuilder.getLine(), tdb.getLine());
719             }
720         }
721         super.addTypedef(typedefBuilder);
722     }
723
724     public TypeDefinitionBuilderImpl addTypedef(final int line, final QName qname, final SchemaPath path) {
725         final TypeDefinitionBuilderImpl builder = new TypeDefinitionBuilderImpl(name, line, qname, path);
726
727         Builder parent = getActualNode();
728         builder.setParent(parent);
729
730         String typedefName = qname.getLocalName();
731         if (parent.equals(this)) {
732             addTypedef(builder);
733         } else {
734             if (parent instanceof DataNodeContainerBuilder) {
735                 DataNodeContainerBuilder parentNode = (DataNodeContainerBuilder) parent;
736                 for (TypeDefinitionBuilder child : parentNode.getTypeDefinitionBuilders()) {
737                     if (child.getQName().getLocalName().equals(typedefName)) {
738                         raiseYangParserException("", "typedef", typedefName, line, child.getLine());
739                     }
740                 }
741                 parentNode.addTypedef(builder);
742             } else if (parent instanceof RpcDefinitionBuilder) {
743                 RpcDefinitionBuilder rpcParent = (RpcDefinitionBuilder) parent;
744                 for (TypeDefinitionBuilder tdb : rpcParent.getTypeDefinitions()) {
745                     if (tdb.getQName().getLocalName().equals(builder.getQName().getLocalName())) {
746                         raiseYangParserException("", "typedef", typedefName, line, tdb.getLine());
747                     }
748                 }
749                 rpcParent.addTypedef(builder);
750             } else {
751                 throw new YangParseException(name, line, "Unresolved parent of typedef " + typedefName);
752             }
753         }
754
755         return builder;
756     }
757
758     public void setType(final TypeDefinition<?> type) {
759         Builder parent = getActualNode();
760         if (!(parent instanceof TypeAwareBuilder)) {
761             throw new YangParseException("Failed to set type '" + type.getQName().getLocalName()
762                     + "'. Invalid parent node: " + parent);
763         }
764         ((TypeAwareBuilder) parent).setType(type);
765     }
766
767     public UnionTypeBuilder addUnionType(final int line, final URI namespace, final Date revision) {
768         final Builder parent = getActualNode();
769         if (parent == null) {
770             throw new YangParseException(name, line, "Unresolved parent of union type");
771         } else {
772             final UnionTypeBuilder union = new UnionTypeBuilder(name, line);
773             if (parent instanceof TypeAwareBuilder) {
774                 ((TypeAwareBuilder) parent).setTypedef(union);
775                 return union;
776             } else {
777                 throw new YangParseException(name, line, "Invalid parent of union type.");
778             }
779         }
780     }
781
782     public void addIdentityrefType(final int line, final SchemaPath schemaPath, final String baseString) {
783         final IdentityrefTypeBuilder identityref = new IdentityrefTypeBuilder(name, line, baseString, schemaPath);
784
785         final Builder parent = getActualNode();
786         if (parent == null) {
787             throw new YangParseException(name, line, "Unresolved parent of identityref type.");
788         } else {
789             if (parent instanceof TypeAwareBuilder) {
790                 final TypeAwareBuilder typeParent = (TypeAwareBuilder) parent;
791                 typeParent.setTypedef(identityref);
792                 dirtyNodes.add(typeParent);
793             } else {
794                 throw new YangParseException(name, line, "Invalid parent of identityref type.");
795             }
796         }
797     }
798
799     public DeviationBuilder addDeviation(final int line, final String targetPath) {
800         Builder parent = getActualNode();
801         if (!(parent.equals(this))) {
802             throw new YangParseException(name, line, "deviation can be defined only in module or submodule");
803         }
804
805         final DeviationBuilder builder = new DeviationBuilder(name, line, targetPath);
806         builder.setParent(parent);
807         deviationBuilders.add(builder);
808         return builder;
809     }
810
811     public IdentitySchemaNodeBuilder addIdentity(final QName qname, final int line, final SchemaPath path) {
812         Builder parent = getActualNode();
813         if (!(parent.equals(this))) {
814             throw new YangParseException(name, line, "identity can be defined only in module or submodule");
815         }
816         String identityName = qname.getLocalName();
817         for (IdentitySchemaNodeBuilder idBuilder : addedIdentities) {
818             if (idBuilder.getQName().equals(qname)) {
819                 raiseYangParserException("", "identity", identityName, line, idBuilder.getLine());
820             }
821         }
822
823         final IdentitySchemaNodeBuilder builder = new IdentitySchemaNodeBuilder(name, line, qname, path);
824         builder.setParent(parent);
825         addedIdentities.add(builder);
826         return builder;
827     }
828
829     @Override
830     public void addUnknownNodeBuilder(final UnknownSchemaNodeBuilder builder) {
831         addedUnknownNodes.add(builder);
832         allUnknownNodes.add(builder);
833     }
834
835     public UnknownSchemaNodeBuilderImpl addUnknownSchemaNode(final int line, final QName qname, final SchemaPath path) {
836         final Builder parent = getActualNode();
837         final UnknownSchemaNodeBuilderImpl builder = new UnknownSchemaNodeBuilderImpl(name, line, qname, path);
838         builder.setParent(parent);
839         allUnknownNodes.add(builder);
840
841         if (parent.equals(this)) {
842             addedUnknownNodes.add(builder);
843         } else {
844             if (parent instanceof SchemaNodeBuilder) {
845                 ((SchemaNodeBuilder) parent).addUnknownNodeBuilder(builder);
846             } else if (parent instanceof DataNodeContainerBuilder) {
847                 ((DataNodeContainerBuilder) parent).addUnknownNodeBuilder(builder);
848             } else if (parent instanceof RefineHolderImpl) {
849                 ((RefineHolderImpl) parent).addUnknownNodeBuilder(builder);
850             } else {
851                 throw new YangParseException(name, line, "Unresolved parent of unknown node '" + qname.getLocalName()
852                         + "'");
853             }
854         }
855
856         return builder;
857     }
858
859     public Set<RpcDefinition> getRpcs() {
860         return rpcs;
861     }
862
863     public Set<RpcDefinitionBuilder> getAddedRpcs() {
864         return addedRpcs;
865     }
866
867     public Set<NotificationDefinition> getNotifications() {
868         return notifications;
869     }
870
871     public Set<NotificationBuilder> getAddedNotifications() {
872         return addedNotifications;
873     }
874
875     @Override
876     public String toString() {
877         return "module " + name;
878     }
879
880     public void setSource(ByteSource byteSource) throws IOException {
881         try (InputStream stream = byteSource.openStream()) {
882             setSource(IOUtils.toString(stream));
883         }
884     }
885
886     public void setSource(final String source) {
887         this.source = source;
888     }
889
890     /**
891      * Add child to parent. Method checks for duplicates and add given child
892      * node to parent. If node with same name is found, throws exception. If
893      * parent is null, child node will be added directly to module.
894      *
895      * @param parent
896      * @param child
897      * @param childName
898      */
899     private void addChildToParent(final Builder parent, final DataSchemaNodeBuilder child, final String childName) {
900         final int lineNum = child.getLine();
901         if (parent.equals(this)) {
902             addChildToModule(child, childName, lineNum);
903         } else {
904             addChildToSubnodeOfModule(parent, child, childName, lineNum);
905         }
906     }
907
908     public String getYangVersion() {
909         return yangVersion;
910     }
911
912     public String getContact() {
913         return contact;
914     }
915
916     public String getOrganization() {
917         return organization;
918     }
919
920     /**
921      * Adds child node <code>child</code> to the set of nodes child nodes.
922      *
923      * The method reduces the complexity of the method
924      * {@link #addChildToParent(Builder, DataSchemaNodeBuilder, String)
925      * addChildToParent}.
926      *
927      * @param child
928      *            data schema node builder for child node
929      * @param childName
930      *            string with name of child node
931      * @param lineNum
932      *            line number in YANG file where is the node with the name equal
933      *            to <code>childName</code> is defined
934      */
935     private void addChildToModule(final DataSchemaNodeBuilder child, final String childName, final int lineNum) {
936         // if parent == null => node is defined under module
937         // All leafs, leaf-lists, lists, containers, choices, rpcs,
938         // notifications, and anyxmls defined within a parent node or at the
939         // top level of the module or its submodules share the same
940         // identifier namespace.
941         for (DataSchemaNodeBuilder childNode : getChildNodeBuilders()) {
942             if (childNode.getQName().getLocalName().equals(childName)) {
943                 raiseYangParserException("'" + child + "'", "node", childName, lineNum, childNode.getLine());
944             }
945         }
946         for (RpcDefinitionBuilder rpc : addedRpcs) {
947             if (rpc.getQName().getLocalName().equals(childName)) {
948                 raiseYangParserException("'" + child + "'", "rpc", childName, lineNum, rpc.getLine());
949             }
950         }
951         for (NotificationBuilder notification : addedNotifications) {
952             if (notification.getQName().getLocalName().equals(childName)) {
953                 raiseYangParserException("'" + child + "'", "notification", childName, lineNum, notification.getLine());
954             }
955         }
956         addChildNode(child);
957     }
958
959     /**
960      * Adds child node <code>child</code> to the group of child nodes of the
961      * <code>parent</code>
962      *
963      * The method reduces the complexity of the method
964      * {@link #addChildToParent(Builder, DataSchemaNodeBuilder, String)
965      * addChildToParent}. *
966      *
967      * @param parent
968      *            builder of node which is parent for <code>child</code>
969      * @param child
970      *            data schema node builder for child node
971      * @param childName
972      *            string with name of child node
973      * @param lineNum
974      *            line number in YANG file where is the node with the name equal
975      *            to <code>childName</code> is defined
976      */
977     private void addChildToSubnodeOfModule(final Builder parent, final DataSchemaNodeBuilder child,
978             final String childName, final int lineNum) {
979         // no need for checking rpc and notification because they can be
980         // defined only under module or submodule
981         if (parent instanceof DataNodeContainerBuilder) {
982             DataNodeContainerBuilder parentNode = (DataNodeContainerBuilder) parent;
983             for (DataSchemaNodeBuilder childNode : parentNode.getChildNodeBuilders()) {
984                 if (childNode.getQName().getLocalName().equals(childName)) {
985                     raiseYangParserException("'" + child + "'", "node", childName, lineNum, childNode.getLine());
986                 }
987             }
988             parentNode.addChildNode(child);
989         } else if (parent instanceof ChoiceBuilder) {
990             ChoiceBuilder parentNode = (ChoiceBuilder) parent;
991             for (ChoiceCaseBuilder caseBuilder : parentNode.getCases()) {
992                 if (caseBuilder.getQName().getLocalName().equals(childName)) {
993                     raiseYangParserException("'" + child + "'", "node", childName, lineNum, caseBuilder.getLine());
994                 }
995             }
996             parentNode.addCase(child);
997         } else {
998             throw new YangParseException(name, lineNum, "Unresolved parent of node '" + childName + "'.");
999         }
1000     }
1001
1002     private ModuleImport createModuleImport(final String moduleName, final Date revision, final String prefix) {
1003         final ModuleImport moduleImport = new ModuleImportImpl(moduleName, revision, prefix);
1004         return moduleImport;
1005     }
1006
1007     private void raiseYangParserException(final String cantAddType, final String type, final String name,
1008             final int currentLine, final int duplicateLine) {
1009
1010         StringBuilder msgPrefix = new StringBuilder("");
1011         if (cantAddType != null && !cantAddType.isEmpty()) {
1012             msgPrefix.append("Can not add ");
1013             msgPrefix.append(cantAddType);
1014             msgPrefix.append(": ");
1015         }
1016
1017         String msg = String.format("%s%s with same name '%s' already declared at line %d.", msgPrefix, type, name,
1018                 duplicateLine);
1019         throw new YangParseException(getModuleName(), currentLine, msg);
1020     }
1021
1022     @Override
1023     public int hashCode() {
1024         final int prime = 31;
1025         int result = 1;
1026         result = prime * result + ((name == null) ? 0 : name.hashCode());
1027         result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
1028         result = prime * result + ((revision == null) ? 0 : revision.hashCode());
1029         result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
1030
1031         return result;
1032     }
1033
1034     @Override
1035     public boolean equals(final Object obj) {
1036         if (this == obj) {
1037             return true;
1038         }
1039         if (obj == null) {
1040             return false;
1041         }
1042         if (getClass() != obj.getClass()) {
1043             return false;
1044         }
1045         ModuleBuilder other = (ModuleBuilder) obj;
1046         if (name == null) {
1047             if (other.name != null) {
1048                 return false;
1049             }
1050         } else if (!name.equals(other.name)) {
1051             return false;
1052         }
1053         if (namespace == null) {
1054             if (other.namespace != null) {
1055                 return false;
1056             }
1057         } else if (!namespace.equals(other.namespace)) {
1058             return false;
1059         }
1060         if (prefix == null) {
1061             if (other.prefix != null) {
1062                 return false;
1063             }
1064         } else if (!prefix.equals(other.prefix)) {
1065             return false;
1066         }
1067         if (revision == null) {
1068             if (other.revision != null) {
1069                 return false;
1070             }
1071         } else if (!revision.equals(other.revision)) {
1072             return false;
1073         }
1074         return true;
1075     }
1076
1077     public List<UnknownSchemaNode> getExtensionInstances() {
1078         return unknownNodes;
1079     }
1080 }