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