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