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