Merge "BUG-2955: Fix error reporting for wrong YangInstanceIdentifier"
[yangtools.git] / yang / yang-model-export / src / main / java / org / opendaylight / yangtools / yang / model / export / SchemaContextEmitter.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.model.export;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Strings;
14 import com.google.common.primitives.UnsignedInteger;
15 import java.net.URI;
16 import java.util.Date;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Objects;
22 import java.util.Set;
23 import javax.annotation.Nullable;
24 import javax.annotation.concurrent.NotThreadSafe;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.Deviation;
35 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
36 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
37 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
38 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
39 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
45 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
46 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
47 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
48 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
49 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
50 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
52 import org.opendaylight.yangtools.yang.model.api.Status;
53 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
54 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.UsesNode;
56 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
57 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
58 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
59 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
60 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
61 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
63 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
65 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
68 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
69 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
70 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
71 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
72 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
73 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
74 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
75 import org.opendaylight.yangtools.yang.model.util.DerivedType;
76 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
77 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
78
79 @Beta
80 @NotThreadSafe
81 class SchemaContextEmitter {
82
83     private final Rfc6020ModuleWriter writer;
84     private final boolean emitInstantiated;
85     private final boolean emitUses;
86     private final Map<QName, StatementDefinition> extensions;
87
88     SchemaContextEmitter(final Rfc6020ModuleWriter writer, final Map<QName, StatementDefinition> extensions) {
89         this(writer, extensions,false, true);
90     }
91
92     SchemaContextEmitter(final Rfc6020ModuleWriter writer, final Map<QName, StatementDefinition> extensions, final boolean emitInstantiated, final boolean emitUses) {
93         this.writer = Preconditions.checkNotNull(writer);
94         this.emitInstantiated = emitInstantiated;
95         this.emitUses = emitUses;
96         this.extensions = Preconditions.checkNotNull(extensions);
97     }
98
99     static void writeToStatementWriter(final Module module, final SchemaContext ctx, final StatementTextWriter statementWriter) {
100         final Rfc6020ModuleWriter yangSchemaWriter = SchemaToStatementWriterAdaptor.from(statementWriter);
101         final Map<QName, StatementDefinition> extensions = ExtensionStatement.mapFrom(ctx.getExtensions());
102         new SchemaContextEmitter(yangSchemaWriter,extensions).emitModule(module);
103     }
104
105     void emitModule(final Module input) {
106         writer.startModuleNode(input.getName());
107         emitModuleHeader(input);
108         emitLinkageNodes(input);
109         emitMetaNodes(input);
110         emitRevisionNodes(input);
111         emitBodyNodes(input);
112         writer.endNode();
113     }
114
115     private void emitModuleHeader(final Module input) {
116         emitYangVersionNode(input.getYangVersion());
117         emitNamespace(input.getNamespace());
118         emitPrefixNode(input.getPrefix());
119     }
120
121     @SuppressWarnings("unused")
122     private void emitSubmodule(final String input) {
123         /*
124          * FIXME: BUG-2444:  Implement submodule export
125          *
126          * submoduleHeaderNodes linkageNodes metaNodes revisionNodes bodyNodes
127          * writer.endNode();
128          */
129     }
130
131     @SuppressWarnings("unused")
132     private void emitSubmoduleHeaderNodes(final Module input) {
133         /*
134          * FIXME: BUG-2444:  Implement submodule headers properly
135          *
136          * :yangVersionNode //Optional
137          *
138          * :belongsToNode
139          */
140     }
141
142     private void emitMetaNodes(final Module input) {
143
144         emitOrganizationNode(input.getOrganization()); // FIXME: BUG-2444: Optional
145         emitContact(input.getContact()); // FIXME: BUG-2444: Optional
146         emitDescriptionNode(input.getDescription());
147         emitReferenceNode(input.getReference());
148     }
149
150     private void emitLinkageNodes(final Module input) {
151         for (final ModuleImport importNode : input.getImports()) {
152             emitImport(importNode);
153         }
154         /*
155          * FIXME: BUG-2444:  Emit include statements
156          */
157     }
158
159     private void emitRevisionNodes(final Module input) {
160         /*
161          * FIXME: BUG-2444:  emit revisions properly, when parsed model will provide enough
162          * information
163          */
164         emitRevision(input.getRevision());
165
166     }
167
168     private void emitBodyNodes(final Module input) {
169
170         for (final ExtensionDefinition extension : input.getExtensionSchemaNodes()) {
171             emitExtension(extension);
172         }
173         for (final FeatureDefinition definition : input.getFeatures()) {
174             emitFeature(definition);
175         }
176         for (final IdentitySchemaNode identity : input.getIdentities()) {
177             emitIdentity(identity);
178         }
179
180         emitDataNodeContainer(input);
181
182         for (final AugmentationSchema augmentation : input.getAugmentations()) {
183             emitAugment(augmentation);
184         }
185         for (final RpcDefinition rpc : input.getRpcs()) {
186             emitRpc(rpc);
187         }
188         for (final NotificationDefinition notification : input.getNotifications()) {
189             emitNotificationNode(notification);
190         }
191         for (final Deviation deviation : input.getDeviations()) {
192             emitDeviation(deviation);
193         }
194
195     }
196
197     private void emitDataNodeContainer(final DataNodeContainer input) {
198         for (final TypeDefinition<?> typedef : input.getTypeDefinitions()) {
199             emitTypedefNode(typedef);
200         }
201         for (final GroupingDefinition grouping : input.getGroupings()) {
202             emitGrouping(grouping);
203         }
204         for (final DataSchemaNode child : input.getChildNodes()) {
205             emitDataSchemaNode(child);
206         }
207         for (final UsesNode usesNode : input.getUses()) {
208             emitUsesNode(usesNode);
209         }
210     }
211
212     private void emitDataSchemaNode(final DataSchemaNode child) {
213         if (!emitInstantiated && (child.isAddedByUses() || child.isAugmenting())) {
214             // We skip instantiated nodes.
215             return;
216         }
217
218         if (child instanceof ContainerSchemaNode) {
219             emitContainer((ContainerSchemaNode) child);
220         } else if (child instanceof LeafSchemaNode) {
221             emitLeaf((LeafSchemaNode) child);
222         } else if (child instanceof LeafListSchemaNode) {
223             emitLeafList((LeafListSchemaNode) child);
224         } else if (child instanceof ListSchemaNode) {
225             emitList((ListSchemaNode) child);
226         } else if (child instanceof ChoiceSchemaNode) {
227             emitChoice((ChoiceSchemaNode) child);
228         } else if (child instanceof AnyXmlSchemaNode) {
229             emitAnyxml((AnyXmlSchemaNode) child);
230         } else {
231             throw new UnsupportedOperationException("Not supported DataSchemaNode type " + child.getClass());
232         }
233     }
234
235     private void emitYangVersionNode(final String input) {
236         writer.startYangVersionNode(input);
237         writer.endNode();
238     }
239
240     private void emitImport(final ModuleImport importNode) {
241         writer.startImportNode(importNode.getModuleName());
242         emitPrefixNode(importNode.getPrefix());
243         emitRevisionDateNode(importNode.getRevision());
244         writer.endNode();
245     }
246
247     @SuppressWarnings("unused")
248     private void emitInclude(final String input) {
249         /*
250          * FIXME: BUG-2444:  Implement proper export of include statements
251          * startIncludeNode(IdentifierHelper.getIdentifier(String :input));
252          *
253          *
254          * :revisionDateNode :writer.endNode();)
255          */
256     }
257
258     private void emitNamespace(final URI uri) {
259         writer.startNamespaceNode(uri);
260         writer.endNode();
261
262     }
263
264     private void emitPrefixNode(final String input) {
265         writer.startPrefixNode(input);
266         writer.endNode();
267
268     }
269
270     @SuppressWarnings("unused")
271     private void emitBelongsTo(final String input) {
272         /*
273          * FIXME: BUG-2444:  Implement proper export of belongs-to statements
274          * startIncludeNode(IdentifierHelper.getIdentifier(String :input));
275          *
276          *
277          * :writer.startBelongsToNode(IdentifierHelper.getIdentifier(String
278          * :input));
279          *
280          *
281          * :prefixNode
282          * :writer.endNode();
283          *
284          */
285
286     }
287
288     private void emitOrganizationNode(final String input) {
289         writer.startOrganizationNode(input);
290         writer.endNode();
291
292     }
293
294     private void emitContact(final String input) {
295         writer.startContactNode(input);
296         writer.endNode();
297
298     }
299
300     private void emitDescriptionNode(@Nullable final String input) {
301         if (!Strings.isNullOrEmpty(input)) {
302             writer.startDescriptionNode(input);
303             writer.endNode();
304         }
305     }
306
307     private void emitReferenceNode(@Nullable final String input) {
308         if (!Strings.isNullOrEmpty(input)) {
309             writer.startReferenceNode(input);
310             writer.endNode();
311         }
312     }
313
314     private void emitUnitsNode(@Nullable final String input) {
315         if (!Strings.isNullOrEmpty(input)) {
316             writer.startUnitsNode(input);
317             writer.endNode();
318         }
319     }
320
321     private void emitRevision(final Date date) {
322         writer.startRevisionNode(date);
323
324         //
325         // FIXME: BUG-2444: FIXME: BUG-2444: BUG-2417: descriptionNode //FIXME: BUG-2444: Optional
326         // FIXME: BUG-2444: FIXME: BUG-2444: BUG-2417: referenceNode //FIXME: BUG-2444: Optional
327         writer.endNode();
328
329     }
330
331     private void emitRevisionDateNode(@Nullable final Date date) {
332         if (date != null) {
333             writer.startRevisionDateNode(date);
334             writer.endNode();
335         }
336     }
337
338     private void emitExtension(final ExtensionDefinition extension) {
339         writer.startExtensionNode(extension.getQName());
340         emitArgument(extension.getArgument(),extension.isYinElement());
341         emitStatusNode(extension.getStatus());
342         emitDescriptionNode(extension.getDescription());
343         emitReferenceNode(extension.getReference());
344         emitUnknownStatementNodes(extension.getUnknownSchemaNodes());
345         writer.endNode();
346
347     }
348
349     private void emitArgument(final @Nullable String input, final boolean yinElement) {
350         if (input != null) {
351             writer.startArgumentNode(input);
352             emitYinElement(yinElement);
353             writer.endNode();
354         }
355
356     }
357
358     private void emitYinElement(final boolean yinElement) {
359         writer.startYinElementNode(yinElement);
360         writer.endNode();
361
362     }
363
364     private void emitIdentity(final IdentitySchemaNode identity) {
365         writer.startIdentityNode(identity.getQName());
366         if (identity.getBaseIdentity() != null) {
367             emitBase(identity.getBaseIdentity().getQName());
368         }
369         emitStatusNode(identity.getStatus());
370         emitDescriptionNode(identity.getDescription());
371         emitReferenceNode(identity.getReference());
372         writer.endNode();
373
374     }
375
376     private void emitBase(final QName qName) {
377         writer.startBaseNode(qName);
378         writer.endNode();
379
380     }
381
382     private void emitFeature(final FeatureDefinition definition) {
383         writer.startFeatureNode(definition.getQName());
384
385         // FIXME: BUG-2444: FIXME: BUG-2444:  Expose ifFeature *(ifFeatureNode )
386         emitStatusNode(definition.getStatus());
387         emitDescriptionNode(definition.getDescription());
388         emitReferenceNode(definition.getReference());
389         writer.endNode();
390
391     }
392
393     @SuppressWarnings("unused")
394     private void emitIfFeature(final String input) {
395         /*
396          * FIXME: BUG-2444:  Implement proper export of include statements
397          * startIncludeNode(IdentifierHelper.getIdentifier(String :input));
398          *
399          */
400     }
401
402     private void emitTypedefNode(final TypeDefinition<?> typedef) {
403         writer.startTypedefNode(typedef.getQName());
404         // Differentiate between derived type and existing type
405         // name.
406         emitTypeNodeDerived(typedef);
407         emitUnitsNode(typedef.getUnits());
408         emitDefaultNode(typedef.getDefaultValue());
409         emitStatusNode(typedef.getStatus());
410         emitDescriptionNode(typedef.getDescription());
411         emitReferenceNode(typedef.getReference());
412         emitUnknownStatementNodes(typedef.getUnknownSchemaNodes());
413         writer.endNode();
414
415     }
416
417     private void emitTypeNode(final SchemaPath parentPath, final TypeDefinition<?> subtype) {
418         final SchemaPath path = subtype.getPath();
419         if (isPrefix(parentPath.getPathFromRoot(), path.getPathFromRoot())) {
420             emitTypeNodeDerived(subtype);
421         } else {
422             emitTypeNodeReferenced(subtype);
423         }
424     }
425
426     private void emitTypeNodeReferenced(final TypeDefinition<?> typeDefinition) {
427         writer.startTypeNode(typeDefinition.getQName());
428         writer.endNode();
429
430     }
431
432     private void emitTypeNodeDerived(final TypeDefinition<?> typeDefinition) {
433         final TypeDefinition<?> baseType;
434         if (typeDefinition.getBaseType() != null) {
435             baseType = typeDefinition.getBaseType();
436         } else {
437             baseType = typeDefinition;
438         }
439         writer.startTypeNode(baseType.getQName());
440         emitTypeBodyNodes(typeDefinition);
441         writer.endNode();
442
443     }
444
445     private void emitTypeBodyNodes(final TypeDefinition<?> typeDef) {
446         if (typeDef instanceof ExtendedType) {
447             emitTypeBodyNodes(DerivedType.from((ExtendedType) typeDef));
448         } else if (typeDef instanceof UnsignedIntegerTypeDefinition) {
449             emitUnsignedIntegerSpecification((UnsignedIntegerTypeDefinition) typeDef);
450         } else if (typeDef instanceof IntegerTypeDefinition) {
451             emitIntegerSpefication((IntegerTypeDefinition) typeDef);
452         } else if (typeDef instanceof DecimalTypeDefinition) {
453             emitDecimal64Specification((DecimalTypeDefinition) typeDef);
454         } else if (typeDef instanceof StringTypeDefinition) {
455             emitStringRestrictions((StringTypeDefinition) typeDef);
456         } else if (typeDef instanceof EnumTypeDefinition) {
457             emitEnumSpecification((EnumTypeDefinition) typeDef);
458         } else if (typeDef instanceof LeafrefTypeDefinition) {
459             emitLeafrefSpecification((LeafrefTypeDefinition) typeDef);
460         } else if (typeDef instanceof IdentityrefTypeDefinition) {
461             emitIdentityrefSpecification((IdentityrefTypeDefinition) typeDef);
462         } else if (typeDef instanceof InstanceIdentifierTypeDefinition) {
463             emitInstanceIdentifierSpecification((InstanceIdentifierTypeDefinition) typeDef);
464         } else if (typeDef instanceof BitsTypeDefinition) {
465             emitBitsSpecification((BitsTypeDefinition) typeDef);
466         } else if (typeDef instanceof UnionTypeDefinition) {
467             emitUnionSpecification((UnionTypeDefinition) typeDef);
468         } else if (typeDef instanceof BinaryTypeDefinition) {
469             // FIXME: BUG-2444:  Is this realy NOOP?
470             // should at least support length statement
471         } else if (typeDef instanceof BooleanTypeDefinition || typeDef instanceof EmptyTypeDefinition) {
472             // NOOP
473         } else {
474             throw new IllegalArgumentException("Not supported type " + typeDef.getClass());
475         }
476     }
477
478     private void emitIntegerSpefication(final IntegerTypeDefinition typeDef) {
479         emitRangeNodeOptional(typeDef.getRangeConstraints());
480     }
481
482     private void emitUnsignedIntegerSpecification(final UnsignedIntegerTypeDefinition typeDef) {
483         emitRangeNodeOptional(typeDef.getRangeConstraints());
484
485     }
486
487     private void emitRangeNodeOptional(final List<RangeConstraint> list) {
488         // FIXME: BUG-2444:  Wrong decomposition in API, should be LenghtConstraint
489         // which contains ranges.
490         if (!list.isEmpty()) {
491             writer.startRangeNode(toRangeString(list));
492             final RangeConstraint first = list.iterator().next();
493             emitErrorMessageNode(first.getErrorMessage());
494             emitErrorAppTagNode(first.getErrorAppTag());
495             emitDescriptionNode(first.getDescription());
496             emitReferenceNode(first.getReference());
497             writer.endNode();
498         }
499
500     }
501
502     private void emitDecimal64Specification(final DecimalTypeDefinition typeDefinition) {
503         emitFranctionDigitsNode(typeDefinition.getFractionDigits());
504         emitRangeNodeOptional(typeDefinition.getRangeConstraints());
505
506     }
507
508     private void emitFranctionDigitsNode(final Integer fractionDigits) {
509         writer.startFractionDigitsNode(fractionDigits);
510         writer.endNode();
511     }
512
513     private void emitStringRestrictions(final StringTypeDefinition typeDef) {
514
515         // FIXME: BUG-2444:  Wrong decomposition in API, should be LenghtConstraint
516         // which contains ranges.
517         emitLength(typeDef.getLengthConstraints());
518
519         for (final PatternConstraint pattern : typeDef.getPatternConstraints()) {
520             emitPatternNode(pattern);
521         }
522
523     }
524
525     private void emitLength(final List<LengthConstraint> list) {
526         if (!list.isEmpty()) {
527             writer.startLengthNode(toLengthString(list));
528             // FIXME: BUG-2444:  Workaround for incorrect decomposition in API
529             final LengthConstraint first = list.iterator().next();
530             emitErrorMessageNode(first.getErrorMessage());
531             emitErrorAppTagNode(first.getErrorAppTag());
532             emitDescriptionNode(first.getDescription());
533             emitReferenceNode(first.getReference());
534             writer.endNode();
535         }
536
537     }
538
539     private String toLengthString(final List<LengthConstraint> list) {
540         final StringBuilder lengthStr = new StringBuilder();
541         final Iterator<LengthConstraint> constIt = list.iterator();
542         while (constIt.hasNext()) {
543             final LengthConstraint current = constIt.next();
544             if (current.getMin() == current.getMax()) {
545                 lengthStr.append(current.getMin());
546             } else {
547                 lengthStr.append(current.getMin());
548                 lengthStr.append("..");
549                 lengthStr.append(current.getMax());
550             }
551             if (constIt.hasNext()) {
552                 lengthStr.append("|");
553             }
554         }
555         return lengthStr.toString();
556     }
557
558     private String toRangeString(final List<RangeConstraint> list) {
559         final StringBuilder lengthStr = new StringBuilder();
560         final Iterator<RangeConstraint> constIt = list.iterator();
561         while (constIt.hasNext()) {
562             final RangeConstraint current = constIt.next();
563             if (current.getMin() == current.getMax()) {
564                 lengthStr.append(current.getMin());
565             } else {
566                 lengthStr.append(current.getMin());
567                 lengthStr.append("..");
568                 lengthStr.append(current.getMax());
569             }
570             if (constIt.hasNext()) {
571                 lengthStr.append("|");
572             }
573         }
574         return lengthStr.toString();
575     }
576
577     private void emitPatternNode(final PatternConstraint pattern) {
578         writer.startPatternNode(pattern.getRegularExpression());
579         emitErrorMessageNode(pattern.getErrorMessage()); // FIXME: BUG-2444: Optional
580         emitErrorAppTagNode(pattern.getErrorAppTag()); // FIXME: BUG-2444: Optional
581         emitDescriptionNode(pattern.getDescription());
582         emitReferenceNode(pattern.getReference()); // FIXME: BUG-2444: Optional
583         writer.endNode();
584     }
585
586     private void emitDefaultNode(@Nullable final Object object) {
587         if (object != null) {
588             writer.startDefaultNode(object.toString());
589             writer.endNode();
590         }
591
592     }
593
594     private void emitEnumSpecification(final EnumTypeDefinition typeDefinition) {
595         for (final EnumPair enumValue : typeDefinition.getValues()) {
596             emitEnumNode(enumValue);
597         }
598     }
599
600     private void emitEnumNode(final EnumPair enumValue) {
601         writer.startEnumNode(enumValue.getName());
602         emitValueNode(enumValue.getValue());
603         emitStatusNode(enumValue.getStatus());
604         emitDescriptionNode(enumValue.getDescription());
605         emitReferenceNode(enumValue.getReference());
606         writer.endNode();
607     }
608
609     private void emitLeafrefSpecification(final LeafrefTypeDefinition typeDefinition) {
610         emitPathNode(typeDefinition.getPathStatement());
611         // FIXME: BUG-2444: requireInstanceNode /Optional removed with (RFC6020 - Errata ID
612         // 2949)
613         // Added in Yang 1.1
614     }
615
616     private void emitPathNode(final RevisionAwareXPath revisionAwareXPath) {
617         writer.startPathNode(revisionAwareXPath);
618         writer.endNode();
619     }
620
621     private void emitRequireInstanceNode(final boolean require) {
622         writer.startRequireInstanceNode(require);
623         writer.endNode();
624     }
625
626     private void emitInstanceIdentifierSpecification(final InstanceIdentifierTypeDefinition typeDefinition) {
627         emitRequireInstanceNode(typeDefinition.requireInstance());
628     }
629
630     private void emitIdentityrefSpecification(final IdentityrefTypeDefinition typeDefinition) {
631         emitBase(typeDefinition.getQName());
632     }
633
634     private void emitUnionSpecification(final UnionTypeDefinition typeDefinition) {
635         for (final TypeDefinition<?> subtype : typeDefinition.getTypes()) {
636             // FIXME: BUG-2444:  What if we have locally modified types here?
637             // is solution to look-up in schema path?
638             emitTypeNode(typeDefinition.getPath(), subtype);
639         }
640     }
641
642     private void emitBitsSpecification(final BitsTypeDefinition typeDefinition) {
643         for (final Bit bit : typeDefinition.getBits()) {
644             emitBit(bit);
645         }
646     }
647
648     private void emitBit(final Bit bit) {
649         writer.startBitNode(bit.getName());
650         emitPositionNode(bit.getPosition());
651         emitStatusNode(bit.getStatus());
652         emitDescriptionNode(bit.getDescription());
653         emitReferenceNode(bit.getReference());
654         writer.endNode();
655     }
656
657     private void emitPositionNode(@Nullable final Long position) {
658         if (position != null) {
659             writer.startPositionNode(UnsignedInteger.valueOf(position));
660             writer.endNode();
661         }
662     }
663
664     private void emitStatusNode(@Nullable final Status status) {
665         if (status != null) {
666             writer.startStatusNode(status);
667             writer.endNode();
668         }
669     }
670
671     private void emitConfigNode(final boolean config) {
672         writer.startConfigNode(config);
673         writer.endNode();
674     }
675
676     private void emitMandatoryNode(final boolean mandatory) {
677         writer.startMandatoryNode(mandatory);
678         writer.endNode();
679     }
680
681     private void emitPresenceNode(final boolean presence) {
682         writer.startPresenceNode(presence);
683         writer.endNode();
684     }
685
686     private void emitOrderedBy(final boolean userOrdered) {
687         if (userOrdered) {
688             writer.startOrderedByNode("user");
689         } else {
690             writer.startOrderedByNode("system");
691         }
692         writer.endNode();
693     }
694
695     private void emitMust(@Nullable final MustDefinition mustCondition) {
696         if(mustCondition != null && mustCondition.getXpath() != null) {
697             writer.startMustNode(mustCondition.getXpath());
698             emitErrorMessageNode(mustCondition.getErrorMessage());
699             emitErrorAppTagNode(mustCondition.getErrorAppTag());
700             emitDescriptionNode(mustCondition.getDescription());
701             emitReferenceNode(mustCondition.getReference());
702             writer.endNode();
703         }
704
705     }
706
707     private void emitErrorMessageNode(@Nullable final String input) {
708         if (input != null && !input.isEmpty()) {
709             writer.startErrorMessageNode(input);
710             writer.endNode();
711         }
712     }
713
714     private void emitErrorAppTagNode(final String input) {
715         if (input != null && !input.isEmpty()) {
716             writer.startErrorAppTagNode(input);
717             writer.endNode();
718         }
719     }
720
721     private void emitMinElementsNode(final Integer min) {
722         if (min != null) {
723             writer.startMinElementsNode(min);
724             writer.endNode();
725         }
726     }
727
728     private void emitMaxElementsNode(final Integer max) {
729         if (max != null) {
730             writer.startMaxElementsNode(max);
731             writer.endNode();
732         }
733     }
734
735     private void emitValueNode(@Nullable final Integer value) {
736         if (value != null) {
737             writer.startValueNode(value);
738             writer.endNode();
739         }
740     }
741
742     private void emitDocumentedNode(final DocumentedNode input) {
743         emitStatusNode(input.getStatus());
744         emitDescriptionNode(input.getDescription());
745         emitReferenceNode(input.getReference());
746     }
747
748     private void emitGrouping(final GroupingDefinition grouping) {
749         writer.startGroupingNode(grouping.getQName());
750         emitDocumentedNode(grouping);
751         emitDataNodeContainer(grouping);
752         emitUnknownStatementNodes(grouping.getUnknownSchemaNodes());
753         writer.endNode();
754
755     }
756
757     private void emitContainer(final ContainerSchemaNode child) {
758         writer.startContainerNode(child.getQName());
759
760         //
761
762         emitConstraints(child.getConstraints());
763         // FIXME: BUG-2444: whenNode //:Optional
764         // FIXME: BUG-2444: *(ifFeatureNode )
765         emitMustNodes(child.getConstraints().getMustConstraints());
766         emitPresenceNode(child.isPresenceContainer());
767         emitConfigNode(child.isConfiguration());
768         emitDocumentedNode(child);
769         emitDataNodeContainer(child);
770         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
771         writer.endNode();
772
773     }
774
775     private void emitConstraints(final ConstraintDefinition constraints) {
776         emitWhen(constraints.getWhenCondition());
777         for (final MustDefinition mustCondition : constraints.getMustConstraints()) {
778             emitMust(mustCondition);
779         }
780
781     }
782
783     private void emitLeaf(final LeafSchemaNode child) {
784         writer.startLeafNode(child.getQName());
785         emitWhen(child.getConstraints().getWhenCondition());
786         // FIXME: BUG-2444:  *(ifFeatureNode )
787         emitTypeNode(child.getPath(), child.getType());
788         emitUnitsNode(child.getUnits());
789         emitMustNodes(child.getConstraints().getMustConstraints());
790         emitDefaultNode(child.getDefault());
791         emitConfigNode(child.isConfiguration());
792         emitMandatoryNode(child.getConstraints().isMandatory());
793         emitDocumentedNode(child);
794         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
795         writer.endNode();
796
797     }
798
799     private void emitLeafList(final LeafListSchemaNode child) {
800         writer.startLeafListNode(child.getQName());
801
802         emitWhen(child.getConstraints().getWhenCondition());
803         // FIXME: BUG-2444: *(ifFeatureNode )
804         emitTypeNode(child.getPath(), child.getType());
805         // FIXME: BUG-2444: unitsNode /Optional
806         emitMustNodes(child.getConstraints().getMustConstraints());
807         emitConfigNode(child.isConfiguration());
808
809         emitMinElementsNode(child.getConstraints().getMinElements());
810         emitMaxElementsNode(child.getConstraints().getMaxElements());
811         emitOrderedBy(child.isUserOrdered());
812         emitDocumentedNode(child);
813         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
814         writer.endNode();
815
816     }
817
818     private void emitList(final ListSchemaNode child) {
819         writer.startListNode(child.getQName());
820         emitWhen(child.getConstraints().getWhenCondition());
821
822         // FIXME: BUG-2444: *(ifFeatureNode )
823         emitMustNodes(child.getConstraints().getMustConstraints());
824         emitKey(child.getKeyDefinition());
825         // FIXME: BUG-2444: *(uniqueNode )
826         emitConfigNode(child.isConfiguration());
827         emitMinElementsNode(child.getConstraints().getMinElements());
828         emitMaxElementsNode(child.getConstraints().getMaxElements());
829         emitOrderedBy(child.isUserOrdered());
830         emitDocumentedNode(child);
831         emitDataNodeContainer(child);
832         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
833         writer.endNode();
834
835     }
836
837     private void emitMustNodes(final Set<MustDefinition> mustConstraints) {
838         for (final MustDefinition must : mustConstraints) {
839             emitMust(must);
840         }
841     }
842
843     private void emitKey(final List<QName> keyList) {
844         if (keyList != null && !keyList.isEmpty()) {
845             writer.startKeyNode(keyList);
846             writer.endNode();
847         }
848     }
849
850     @SuppressWarnings("unused")
851     private void emitUnique(final String input) {
852         // FIXME: BUG-2444: writer.startUniqueNode(uniqueArgStr)); Nodeend
853
854     }
855
856     private void emitChoice(final ChoiceSchemaNode choice) {
857         writer.startChoiceNode(choice.getQName());
858         emitWhen(choice.getConstraints().getWhenCondition());
859         // FIXME: BUG-2444: *(ifFeatureNode )
860         // FIXME: BUG-2444: defaultNode //Optional
861         emitConfigNode(choice.isConfiguration());
862         emitMandatoryNode(choice.getConstraints().isMandatory());
863         emitDocumentedNode(choice);
864         for (final ChoiceCaseNode caze : choice.getCases()) {
865             // TODO: emit short case?
866             emitCaseNode(caze);
867         }
868         emitUnknownStatementNodes(choice.getUnknownSchemaNodes());
869         writer.endNode();
870     }
871
872     private void emitCaseNode(final ChoiceCaseNode caze) {
873         if (!emitInstantiated && caze.isAugmenting()) {
874             return;
875         }
876         writer.startCaseNode(caze.getQName());
877         emitWhen(caze.getConstraints().getWhenCondition());
878         // FIXME: BUG-2444: *(ifFeatureNode )
879         emitDocumentedNode(caze);
880         emitDataNodeContainer(caze);
881         emitUnknownStatementNodes(caze.getUnknownSchemaNodes());
882         writer.endNode();
883
884     }
885
886     private void emitAnyxml(final AnyXmlSchemaNode child) {
887         writer.startAnyxmlNode(child.getQName());
888
889         emitWhen(child.getConstraints().getWhenCondition());
890         // FIXME: BUG-2444: *(ifFeatureNode )
891         emitMustNodes(child.getConstraints().getMustConstraints());
892         emitConfigNode(child.isConfiguration());
893         emitMandatoryNode(child.getConstraints().isMandatory());
894         emitDocumentedNode(child);
895         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
896         writer.endNode();
897
898     }
899
900     private void emitUsesNode(final UsesNode usesNode) {
901         if (emitUses && !usesNode.isAddedByUses() && !usesNode.isAugmenting()) {
902             writer.startUsesNode(usesNode.getGroupingPath().getLastComponent());
903             /*
904              * FIXME: BUG-2444:
905              *  whenNode /
906              *  *(ifFeatureNode )
907              * statusNode // Optional F
908              * : descriptionNode // Optional
909              * referenceNode // Optional
910              */
911             for (final Entry<SchemaPath, SchemaNode> refine : usesNode.getRefines().entrySet()) {
912                 emitRefine(refine);
913             }
914             for (final AugmentationSchema aug : usesNode.getAugmentations()) {
915                 emitUsesAugmentNode(aug);
916             }
917             writer.endNode();
918         }
919     }
920
921     private void emitRefine(final Entry<SchemaPath, SchemaNode> refine) {
922         final SchemaPath path = refine.getKey();
923         final SchemaNode value = refine.getValue();
924         writer.startRefineNode(path);
925
926         if (value instanceof LeafSchemaNode) {
927             emitRefineLeafNodes((LeafSchemaNode) value);
928         } else if (value instanceof LeafListSchemaNode) {
929             emitRefineLeafListNodes((LeafListSchemaNode) value);
930         } else if (value instanceof ListSchemaNode) {
931             emitRefineListNodes((ListSchemaNode) value);
932         } else if (value instanceof ChoiceSchemaNode) {
933             emitRefineChoiceNodes((ChoiceSchemaNode) value);
934         } else if (value instanceof ChoiceCaseNode) {
935             emitRefineCaseNodes((ChoiceCaseNode) value);
936         } else if (value instanceof ContainerSchemaNode) {
937             emitRefineContainerNodes((ContainerSchemaNode) value);
938         } else if (value instanceof AnyXmlSchemaNode) {
939             emitRefineAnyxmlNodes((AnyXmlSchemaNode) value);
940         }
941         writer.endNode();
942
943     }
944
945     private <T extends SchemaNode> T getOriginalChecked(final T value) {
946         final Optional<SchemaNode> original = SchemaNodeUtils.getOriginalIfPossible(value);
947         Preconditions.checkArgument(original.isPresent(), "Original unmodified version of node is not present.");
948         @SuppressWarnings("unchecked")
949         final T ret = (T) original.get();
950         return ret;
951     }
952
953     private void emitDocumentedNodeRefine(final DocumentedNode original, final DocumentedNode value) {
954         if (Objects.deepEquals(original.getDescription(), value.getDescription())) {
955             emitDescriptionNode(value.getDescription());
956         }
957         if (Objects.deepEquals(original.getReference(), value.getReference())) {
958             emitReferenceNode(value.getReference());
959         }
960     }
961
962     private void emitRefineContainerNodes(final ContainerSchemaNode value) {
963         final ContainerSchemaNode original = getOriginalChecked(value);
964
965         // emitMustNodes(child.getConstraints().getMustConstraints());
966         if (Objects.deepEquals(original.isPresenceContainer(), value.isPresenceContainer())) {
967             emitPresenceNode(value.isPresenceContainer());
968         }
969         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
970             emitConfigNode(value.isConfiguration());
971         }
972         emitDocumentedNodeRefine(original, value);
973
974     }
975
976     private void emitRefineLeafNodes(final LeafSchemaNode value) {
977         final LeafSchemaNode original = getOriginalChecked(value);
978
979         // emitMustNodes(child.getConstraints().getMustConstraints());
980         if (Objects.deepEquals(original.getDefault(), value.getDefault())) {
981             emitDefaultNode(value.getDefault());
982         }
983         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
984             emitConfigNode(value.isConfiguration());
985         }
986         emitDocumentedNodeRefine(original, value);
987         if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
988             emitMandatoryNode(value.getConstraints().isMandatory());
989         }
990
991     }
992
993     private void emitRefineLeafListNodes(final LeafListSchemaNode value) {
994         final LeafListSchemaNode original = getOriginalChecked(value);
995
996         // emitMustNodes(child.getConstraints().getMustConstraints());
997         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
998             emitConfigNode(value.isConfiguration());
999         }
1000         if (Objects.deepEquals(original.getConstraints().getMinElements(), value.getConstraints().getMinElements())) {
1001             emitMinElementsNode(value.getConstraints().getMinElements());
1002         }
1003         if (Objects.deepEquals(original.getConstraints().getMaxElements(), value.getConstraints().getMaxElements())) {
1004             emitMaxElementsNode(value.getConstraints().getMaxElements());
1005         }
1006         emitDocumentedNodeRefine(original, value);
1007
1008     }
1009
1010     private void emitRefineListNodes(final ListSchemaNode value) {
1011         final ListSchemaNode original = getOriginalChecked(value);
1012
1013         // emitMustNodes(child.getConstraints().getMustConstraints());
1014         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
1015             emitConfigNode(value.isConfiguration());
1016         }
1017         if (Objects.deepEquals(original.getConstraints().getMinElements(), value.getConstraints().getMinElements())) {
1018             emitMinElementsNode(value.getConstraints().getMinElements());
1019         }
1020         if (Objects.deepEquals(original.getConstraints().getMaxElements(), value.getConstraints().getMaxElements())) {
1021             emitMaxElementsNode(value.getConstraints().getMaxElements());
1022         }
1023         emitDocumentedNodeRefine(original, value);
1024
1025     }
1026
1027     private void emitRefineChoiceNodes(final ChoiceSchemaNode value) {
1028         final ChoiceSchemaNode original = getOriginalChecked(value);
1029
1030         // FIXME: BUG-2444: defaultNode //FIXME: BUG-2444: Optional
1031         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
1032             emitConfigNode(value.isConfiguration());
1033         }
1034         if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
1035             emitMandatoryNode(value.getConstraints().isMandatory());
1036         }
1037         emitDocumentedNodeRefine(original, value);
1038
1039     }
1040
1041     private void emitRefineCaseNodes(final ChoiceCaseNode value) {
1042         final ChoiceCaseNode original = getOriginalChecked(value);
1043         emitDocumentedNodeRefine(original, value);
1044
1045     }
1046
1047     private void emitRefineAnyxmlNodes(final AnyXmlSchemaNode value) {
1048         final AnyXmlSchemaNode original = getOriginalChecked(value);
1049
1050         // FIXME: BUG-2444:  emitMustNodes(child.getConstraints().getMustConstraints());
1051         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
1052             emitConfigNode(value.isConfiguration());
1053         }
1054         if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
1055             emitMandatoryNode(value.getConstraints().isMandatory());
1056         }
1057         emitDocumentedNodeRefine(original, value);
1058
1059     }
1060
1061     private void emitUsesAugmentNode(final AugmentationSchema aug) {
1062         /**
1063          * differs only in location in schema, otherwise currently (as of
1064          * RFC6020) it is same, so we could freely reuse path.
1065          */
1066         emitAugment(aug);
1067     }
1068
1069     private void emitAugment(final AugmentationSchema augmentation) {
1070         writer.startAugmentNode(augmentation.getTargetPath());
1071         // FIXME: BUG-2444: whenNode //Optional
1072         // FIXME: BUG-2444: *(ifFeatureNode )
1073
1074         emitStatusNode(augmentation.getStatus());
1075         emitDescriptionNode(augmentation.getDescription());
1076         emitReferenceNode(augmentation.getReference());
1077         for(final UsesNode uses: augmentation.getUses()) {
1078             emitUsesNode(uses);
1079         }
1080
1081         for (final DataSchemaNode childNode : augmentation.getChildNodes()) {
1082             if (childNode instanceof ChoiceCaseNode) {
1083                 emitCaseNode((ChoiceCaseNode) childNode);
1084             } else {
1085                 emitDataSchemaNode(childNode);
1086             }
1087         }
1088         emitUnknownStatementNodes(augmentation.getUnknownSchemaNodes());
1089         writer.endNode();
1090     }
1091
1092     private void emitUnknownStatementNodes(final List<UnknownSchemaNode> unknownNodes) {
1093         for (final UnknownSchemaNode unknonwnNode : unknownNodes) {
1094             emitUnknownStatementNode(unknonwnNode);
1095         }
1096     }
1097
1098     private void emitUnknownStatementNode(final UnknownSchemaNode node) {
1099         final StatementDefinition def = getStatementChecked(node.getNodeType());
1100         if (def.getArgumentName() == null) {
1101             writer.startUnknownNode(def);
1102         } else {
1103             writer.startUnknownNode(def, node.getNodeParameter());
1104         }
1105         emitUnknownStatementNodes(node.getUnknownSchemaNodes());
1106         writer.endNode();
1107     }
1108
1109     private StatementDefinition getStatementChecked(final QName nodeType) {
1110         final StatementDefinition ret = extensions.get(nodeType);
1111         Preconditions.checkArgument(ret != null, "Unknown extension %s used during export.",nodeType);
1112         return ret;
1113     }
1114
1115     private void emitWhen(final RevisionAwareXPath revisionAwareXPath) {
1116         if(revisionAwareXPath != null) {
1117             writer.startWhenNode(revisionAwareXPath);
1118             writer.endNode();
1119         }
1120                 // FIXME: BUG-2444: descriptionNode //FIXME: BUG-2444: Optional
1121         // FIXME: BUG-2444: referenceNode //FIXME: BUG-2444: Optional
1122         // FIXME: BUG-2444: writer.endNode();)
1123
1124     }
1125
1126     private void emitRpc(final RpcDefinition rpc) {
1127         writer.startRpcNode(rpc.getQName());
1128         // FIXME: BUG-2444: *(ifFeatureNode )
1129         emitStatusNode(rpc.getStatus());
1130         emitDescriptionNode(rpc.getDescription());
1131         emitReferenceNode(rpc.getReference());
1132
1133         for(final TypeDefinition<?> typedef : rpc.getTypeDefinitions()) {
1134             emitTypedefNode(typedef);
1135         }
1136         for(final GroupingDefinition grouping : rpc.getGroupings()) {
1137             emitGrouping(grouping);
1138         }
1139         emitInput(rpc.getInput());
1140         emitOutput(rpc.getOutput());
1141         emitUnknownStatementNodes(rpc.getUnknownSchemaNodes());
1142         writer.endNode();
1143
1144     }
1145
1146     private void emitInput(@Nullable final ContainerSchemaNode input) {
1147         if (input != null) {
1148             writer.startInputNode();
1149             emitDataNodeContainer(input);
1150             emitUnknownStatementNodes(input.getUnknownSchemaNodes());
1151             writer.endNode();
1152         }
1153
1154     }
1155
1156     private void emitOutput(@Nullable final ContainerSchemaNode input) {
1157         if (input != null) {
1158             writer.startOutputNode();
1159             emitDataNodeContainer(input);
1160             emitUnknownStatementNodes(input.getUnknownSchemaNodes());
1161             writer.endNode();
1162         }
1163
1164     }
1165
1166     private void emitNotificationNode(final NotificationDefinition notification) {
1167         writer.startNotificationNode(notification.getQName());
1168         // FIXME: BUG-2444: *(ifFeatureNode )
1169         emitDocumentedNode(notification);
1170         emitDataNodeContainer(notification);
1171         emitUnknownStatementNodes(notification.getUnknownSchemaNodes());
1172         writer.endNode();
1173
1174     }
1175
1176
1177     //FIXME: Probably should be moved to utils bundle.
1178     private static <T> boolean  isPrefix(final Iterable<T> prefix, final Iterable<T> other) {
1179         final Iterator<T> prefixIt = prefix.iterator();
1180         final Iterator<T> otherIt = other.iterator();
1181         while(prefixIt.hasNext()) {
1182             if(!otherIt.hasNext()) {
1183                 return false;
1184             }
1185             if(!Objects.deepEquals(prefixIt.next(), otherIt.next())) {
1186                 return false;
1187             }
1188         }
1189         return true;
1190     }
1191
1192     private void emitDeviation(final Deviation deviation) {
1193         /*
1194          * FIXME: BUG-2444:  Deviation is not modeled properly and we are loosing lot of
1195          * information in order to export it properly
1196          *
1197          * writer.startDeviationNode(deviation.getTargetPath());
1198          *
1199          * :descriptionNode //:Optional
1200          *
1201          *
1202          * emitReferenceNode(deviation.getReference());
1203          * :(deviateNotSupportedNode :1*(deviateAddNode :deviateReplaceNode
1204          * :deviateDeleteNode)) :writer.endNode();
1205          */
1206     }
1207 }