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