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