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