Bug 5531: Can't get complete YIN schema on Windows
[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     private static 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 static 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 ChoiceSchemaNode 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 ChoiceSchemaNode) {
932             emitRefineChoiceNodes((ChoiceSchemaNode) 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 static <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 ChoiceSchemaNode value) {
1027         final ChoiceSchemaNode 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             if (!unknonwnNode.isAddedByAugmentation() && !unknonwnNode.isAddedByUses()) {
1094                 emitUnknownStatementNode(unknonwnNode);
1095             }
1096         }
1097     }
1098
1099     private void emitUnknownStatementNode(final UnknownSchemaNode node) {
1100         final StatementDefinition def = getStatementChecked(node.getNodeType());
1101         if (def.getArgumentName() == null) {
1102             writer.startUnknownNode(def);
1103         } else {
1104             writer.startUnknownNode(def, node.getNodeParameter());
1105         }
1106         emitUnknownStatementNodes(node.getUnknownSchemaNodes());
1107         writer.endNode();
1108     }
1109
1110     private StatementDefinition getStatementChecked(final QName nodeType) {
1111         final StatementDefinition ret = extensions.get(nodeType);
1112         Preconditions.checkArgument(ret != null, "Unknown extension %s used during export.",nodeType);
1113         return ret;
1114     }
1115
1116     private void emitWhen(final RevisionAwareXPath revisionAwareXPath) {
1117         if(revisionAwareXPath != null) {
1118             writer.startWhenNode(revisionAwareXPath);
1119             writer.endNode();
1120         }
1121                 // FIXME: BUG-2444: descriptionNode //FIXME: BUG-2444: Optional
1122         // FIXME: BUG-2444: referenceNode //FIXME: BUG-2444: Optional
1123         // FIXME: BUG-2444: writer.endNode();)
1124
1125     }
1126
1127     private void emitRpc(final RpcDefinition rpc) {
1128         writer.startRpcNode(rpc.getQName());
1129         // FIXME: BUG-2444: *(ifFeatureNode )
1130         emitStatusNode(rpc.getStatus());
1131         emitDescriptionNode(rpc.getDescription());
1132         emitReferenceNode(rpc.getReference());
1133
1134         for(final TypeDefinition<?> typedef : rpc.getTypeDefinitions()) {
1135             emitTypedefNode(typedef);
1136         }
1137         for(final GroupingDefinition grouping : rpc.getGroupings()) {
1138             emitGrouping(grouping);
1139         }
1140         emitInput(rpc.getInput());
1141         emitOutput(rpc.getOutput());
1142         emitUnknownStatementNodes(rpc.getUnknownSchemaNodes());
1143         writer.endNode();
1144
1145     }
1146
1147     private void emitInput(@Nullable final ContainerSchemaNode input) {
1148         if (input != null) {
1149             writer.startInputNode();
1150             emitDataNodeContainer(input);
1151             emitUnknownStatementNodes(input.getUnknownSchemaNodes());
1152             writer.endNode();
1153         }
1154
1155     }
1156
1157     private void emitOutput(@Nullable final ContainerSchemaNode input) {
1158         if (input != null) {
1159             writer.startOutputNode();
1160             emitDataNodeContainer(input);
1161             emitUnknownStatementNodes(input.getUnknownSchemaNodes());
1162             writer.endNode();
1163         }
1164
1165     }
1166
1167     private void emitNotificationNode(final NotificationDefinition notification) {
1168         writer.startNotificationNode(notification.getQName());
1169         // FIXME: BUG-2444: *(ifFeatureNode )
1170         emitDocumentedNode(notification);
1171         emitDataNodeContainer(notification);
1172         emitUnknownStatementNodes(notification.getUnknownSchemaNodes());
1173         writer.endNode();
1174
1175     }
1176
1177
1178     //FIXME: Probably should be moved to utils bundle.
1179     private static <T> boolean  isPrefix(final Iterable<T> prefix, final Iterable<T> other) {
1180         final Iterator<T> prefixIt = prefix.iterator();
1181         final Iterator<T> otherIt = other.iterator();
1182         while(prefixIt.hasNext()) {
1183             if(!otherIt.hasNext()) {
1184                 return false;
1185             }
1186             if(!Objects.deepEquals(prefixIt.next(), otherIt.next())) {
1187                 return false;
1188             }
1189         }
1190         return true;
1191     }
1192
1193     private void emitDeviation(final Deviation deviation) {
1194         /*
1195          * FIXME: BUG-2444:  Deviation is not modeled properly and we are loosing lot of
1196          * information in order to export it properly
1197          *
1198          * writer.startDeviationNode(deviation.getTargetPath());
1199          *
1200          * :descriptionNode //:Optional
1201          *
1202          *
1203          * emitReferenceNode(deviation.getReference());
1204          * :(deviateNotSupportedNode :1*(deviateAddNode :deviateReplaceNode
1205          * :deviateDeleteNode)) :writer.endNode();
1206          */
1207     }
1208 }