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