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