baa2dbddee139bf3b810b31f154e4e07bd7e0075
[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.getIdentity().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         emitPresenceNode(child.isPresenceContainer());
761         emitConfigNode(child.isConfiguration());
762         emitDocumentedNode(child);
763         emitDataNodeContainer(child);
764         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
765         writer.endNode();
766
767     }
768
769     private void emitConstraints(final ConstraintDefinition constraints) {
770         emitWhen(constraints.getWhenCondition());
771         for (final MustDefinition mustCondition : constraints.getMustConstraints()) {
772             emitMust(mustCondition);
773         }
774
775     }
776
777     private void emitLeaf(final LeafSchemaNode child) {
778         writer.startLeafNode(child.getQName());
779         emitWhen(child.getConstraints().getWhenCondition());
780         // FIXME: BUG-2444:  *(ifFeatureNode )
781         emitTypeNode(child.getPath(), child.getType());
782         emitUnitsNode(child.getUnits());
783         emitMustNodes(child.getConstraints().getMustConstraints());
784         emitDefaultNode(child.getDefault());
785         emitConfigNode(child.isConfiguration());
786         emitMandatoryNode(child.getConstraints().isMandatory());
787         emitDocumentedNode(child);
788         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
789         writer.endNode();
790
791     }
792
793     private void emitLeafList(final LeafListSchemaNode child) {
794         writer.startLeafListNode(child.getQName());
795
796         emitWhen(child.getConstraints().getWhenCondition());
797         // FIXME: BUG-2444: *(ifFeatureNode )
798         emitTypeNode(child.getPath(), child.getType());
799         // FIXME: BUG-2444: unitsNode /Optional
800         emitMustNodes(child.getConstraints().getMustConstraints());
801         emitConfigNode(child.isConfiguration());
802
803         emitMinElementsNode(child.getConstraints().getMinElements());
804         emitMaxElementsNode(child.getConstraints().getMaxElements());
805         emitOrderedBy(child.isUserOrdered());
806         emitDocumentedNode(child);
807         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
808         writer.endNode();
809
810     }
811
812     private void emitList(final ListSchemaNode child) {
813         writer.startListNode(child.getQName());
814         emitWhen(child.getConstraints().getWhenCondition());
815
816         // FIXME: BUG-2444: *(ifFeatureNode )
817         emitMustNodes(child.getConstraints().getMustConstraints());
818         emitKey(child.getKeyDefinition());
819         // FIXME: BUG-2444: *(uniqueNode )
820         emitConfigNode(child.isConfiguration());
821         emitMinElementsNode(child.getConstraints().getMinElements());
822         emitMaxElementsNode(child.getConstraints().getMaxElements());
823         emitOrderedBy(child.isUserOrdered());
824         emitDocumentedNode(child);
825         emitDataNodeContainer(child);
826         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
827         writer.endNode();
828
829     }
830
831     private void emitMustNodes(final Set<MustDefinition> mustConstraints) {
832         for (final MustDefinition must : mustConstraints) {
833             emitMust(must);
834         }
835     }
836
837     private void emitKey(final List<QName> keyList) {
838         if (keyList != null && !keyList.isEmpty()) {
839             writer.startKeyNode(keyList);
840             writer.endNode();
841         }
842     }
843
844     @SuppressWarnings("unused")
845     private void emitUnique(final String input) {
846         // FIXME: BUG-2444: writer.startUniqueNode(uniqueArgStr)); Nodeend
847
848     }
849
850     private void emitChoice(final ChoiceSchemaNode choice) {
851         writer.startChoiceNode(choice.getQName());
852         emitWhen(choice.getConstraints().getWhenCondition());
853         // FIXME: BUG-2444: *(ifFeatureNode )
854         // FIXME: BUG-2444: defaultNode //Optional
855         emitConfigNode(choice.isConfiguration());
856         emitMandatoryNode(choice.getConstraints().isMandatory());
857         emitDocumentedNode(choice);
858         for (final ChoiceCaseNode caze : choice.getCases()) {
859             // TODO: emit short case?
860             emitCaseNode(caze);
861         }
862         emitUnknownStatementNodes(choice.getUnknownSchemaNodes());
863         writer.endNode();
864     }
865
866     private void emitCaseNode(final ChoiceCaseNode caze) {
867         if (!emitInstantiated && caze.isAugmenting()) {
868             return;
869         }
870         writer.startCaseNode(caze.getQName());
871         emitWhen(caze.getConstraints().getWhenCondition());
872         // FIXME: BUG-2444: *(ifFeatureNode )
873         emitDocumentedNode(caze);
874         emitDataNodeContainer(caze);
875         emitUnknownStatementNodes(caze.getUnknownSchemaNodes());
876         writer.endNode();
877
878     }
879
880     private void emitAnyxml(final AnyXmlSchemaNode child) {
881         writer.startAnyxmlNode(child.getQName());
882
883         emitWhen(child.getConstraints().getWhenCondition());
884         // FIXME: BUG-2444: *(ifFeatureNode )
885         emitMustNodes(child.getConstraints().getMustConstraints());
886         emitConfigNode(child.isConfiguration());
887         emitMandatoryNode(child.getConstraints().isMandatory());
888         emitDocumentedNode(child);
889         emitUnknownStatementNodes(child.getUnknownSchemaNodes());
890         writer.endNode();
891
892     }
893
894     private void emitUsesNode(final UsesNode usesNode) {
895         if (emitUses && !usesNode.isAddedByUses() && !usesNode.isAugmenting()) {
896             writer.startUsesNode(usesNode.getGroupingPath().getLastComponent());
897             /*
898              * FIXME: BUG-2444:
899              *  whenNode /
900              *  *(ifFeatureNode )
901              * statusNode // Optional F
902              * : descriptionNode // Optional
903              * referenceNode // Optional
904              */
905             for (final Entry<SchemaPath, SchemaNode> refine : usesNode.getRefines().entrySet()) {
906                 emitRefine(refine);
907             }
908             for (final AugmentationSchema aug : usesNode.getAugmentations()) {
909                 emitUsesAugmentNode(aug);
910             }
911             writer.endNode();
912         }
913     }
914
915     private void emitRefine(final Entry<SchemaPath, SchemaNode> refine) {
916         final SchemaPath path = refine.getKey();
917         final SchemaNode value = refine.getValue();
918         writer.startRefineNode(path);
919
920         if (value instanceof LeafSchemaNode) {
921             emitRefineLeafNodes((LeafSchemaNode) value);
922         } else if (value instanceof LeafListSchemaNode) {
923             emitRefineLeafListNodes((LeafListSchemaNode) value);
924         } else if (value instanceof ListSchemaNode) {
925             emitRefineListNodes((ListSchemaNode) value);
926         } else if (value instanceof ChoiceSchemaNode) {
927             emitRefineChoiceNodes((ChoiceSchemaNode) value);
928         } else if (value instanceof ChoiceCaseNode) {
929             emitRefineCaseNodes((ChoiceCaseNode) value);
930         } else if (value instanceof ContainerSchemaNode) {
931             emitRefineContainerNodes((ContainerSchemaNode) value);
932         } else if (value instanceof AnyXmlSchemaNode) {
933             emitRefineAnyxmlNodes((AnyXmlSchemaNode) value);
934         }
935         writer.endNode();
936
937     }
938
939     private static <T extends SchemaNode> T getOriginalChecked(final T value) {
940         final Optional<SchemaNode> original = SchemaNodeUtils.getOriginalIfPossible(value);
941         Preconditions.checkArgument(original.isPresent(), "Original unmodified version of node is not present.");
942         @SuppressWarnings("unchecked")
943         final T ret = (T) original.get();
944         return ret;
945     }
946
947     private void emitDocumentedNodeRefine(final DocumentedNode original, final DocumentedNode value) {
948         if (Objects.deepEquals(original.getDescription(), value.getDescription())) {
949             emitDescriptionNode(value.getDescription());
950         }
951         if (Objects.deepEquals(original.getReference(), value.getReference())) {
952             emitReferenceNode(value.getReference());
953         }
954     }
955
956     private void emitRefineContainerNodes(final ContainerSchemaNode value) {
957         final ContainerSchemaNode original = getOriginalChecked(value);
958
959         // emitMustNodes(child.getConstraints().getMustConstraints());
960         if (Objects.deepEquals(original.isPresenceContainer(), value.isPresenceContainer())) {
961             emitPresenceNode(value.isPresenceContainer());
962         }
963         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
964             emitConfigNode(value.isConfiguration());
965         }
966         emitDocumentedNodeRefine(original, value);
967
968     }
969
970     private void emitRefineLeafNodes(final LeafSchemaNode value) {
971         final LeafSchemaNode original = getOriginalChecked(value);
972
973         // emitMustNodes(child.getConstraints().getMustConstraints());
974         if (Objects.deepEquals(original.getDefault(), value.getDefault())) {
975             emitDefaultNode(value.getDefault());
976         }
977         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
978             emitConfigNode(value.isConfiguration());
979         }
980         emitDocumentedNodeRefine(original, value);
981         if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
982             emitMandatoryNode(value.getConstraints().isMandatory());
983         }
984
985     }
986
987     private void emitRefineLeafListNodes(final LeafListSchemaNode value) {
988         final LeafListSchemaNode original = getOriginalChecked(value);
989
990         // emitMustNodes(child.getConstraints().getMustConstraints());
991         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
992             emitConfigNode(value.isConfiguration());
993         }
994         if (Objects.deepEquals(original.getConstraints().getMinElements(), value.getConstraints().getMinElements())) {
995             emitMinElementsNode(value.getConstraints().getMinElements());
996         }
997         if (Objects.deepEquals(original.getConstraints().getMaxElements(), value.getConstraints().getMaxElements())) {
998             emitMaxElementsNode(value.getConstraints().getMaxElements());
999         }
1000         emitDocumentedNodeRefine(original, value);
1001
1002     }
1003
1004     private void emitRefineListNodes(final ListSchemaNode value) {
1005         final ListSchemaNode original = getOriginalChecked(value);
1006
1007         // emitMustNodes(child.getConstraints().getMustConstraints());
1008         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
1009             emitConfigNode(value.isConfiguration());
1010         }
1011         if (Objects.deepEquals(original.getConstraints().getMinElements(), value.getConstraints().getMinElements())) {
1012             emitMinElementsNode(value.getConstraints().getMinElements());
1013         }
1014         if (Objects.deepEquals(original.getConstraints().getMaxElements(), value.getConstraints().getMaxElements())) {
1015             emitMaxElementsNode(value.getConstraints().getMaxElements());
1016         }
1017         emitDocumentedNodeRefine(original, value);
1018
1019     }
1020
1021     private void emitRefineChoiceNodes(final ChoiceSchemaNode value) {
1022         final ChoiceSchemaNode original = getOriginalChecked(value);
1023
1024         // FIXME: BUG-2444: defaultNode //FIXME: BUG-2444: Optional
1025         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
1026             emitConfigNode(value.isConfiguration());
1027         }
1028         if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
1029             emitMandatoryNode(value.getConstraints().isMandatory());
1030         }
1031         emitDocumentedNodeRefine(original, value);
1032
1033     }
1034
1035     private void emitRefineCaseNodes(final ChoiceCaseNode value) {
1036         final ChoiceCaseNode original = getOriginalChecked(value);
1037         emitDocumentedNodeRefine(original, value);
1038
1039     }
1040
1041     private void emitRefineAnyxmlNodes(final AnyXmlSchemaNode value) {
1042         final AnyXmlSchemaNode original = getOriginalChecked(value);
1043
1044         // FIXME: BUG-2444:  emitMustNodes(child.getConstraints().getMustConstraints());
1045         if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
1046             emitConfigNode(value.isConfiguration());
1047         }
1048         if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
1049             emitMandatoryNode(value.getConstraints().isMandatory());
1050         }
1051         emitDocumentedNodeRefine(original, value);
1052
1053     }
1054
1055     private void emitUsesAugmentNode(final AugmentationSchema aug) {
1056         /**
1057          * differs only in location in schema, otherwise currently (as of
1058          * RFC6020) it is same, so we could freely reuse path.
1059          */
1060         emitAugment(aug);
1061     }
1062
1063     private void emitAugment(final AugmentationSchema augmentation) {
1064         writer.startAugmentNode(augmentation.getTargetPath());
1065         // FIXME: BUG-2444: whenNode //Optional
1066         // FIXME: BUG-2444: *(ifFeatureNode )
1067
1068         emitStatusNode(augmentation.getStatus());
1069         emitDescriptionNode(augmentation.getDescription());
1070         emitReferenceNode(augmentation.getReference());
1071         for (final UsesNode uses: augmentation.getUses()) {
1072             emitUsesNode(uses);
1073         }
1074
1075         for (final DataSchemaNode childNode : augmentation.getChildNodes()) {
1076             if (childNode instanceof ChoiceCaseNode) {
1077                 emitCaseNode((ChoiceCaseNode) childNode);
1078             } else {
1079                 emitDataSchemaNode(childNode);
1080             }
1081         }
1082         emitUnknownStatementNodes(augmentation.getUnknownSchemaNodes());
1083         writer.endNode();
1084     }
1085
1086     private void emitUnknownStatementNodes(final List<UnknownSchemaNode> unknownNodes) {
1087         for (final UnknownSchemaNode unknonwnNode : unknownNodes) {
1088             if (!unknonwnNode.isAddedByAugmentation() && !unknonwnNode.isAddedByUses()) {
1089                 emitUnknownStatementNode(unknonwnNode);
1090             }
1091         }
1092     }
1093
1094     private void emitUnknownStatementNode(final UnknownSchemaNode node) {
1095         final StatementDefinition def = getStatementChecked(node.getNodeType());
1096         if (def.getArgumentName() == null) {
1097             writer.startUnknownNode(def);
1098         } else {
1099             writer.startUnknownNode(def, node.getNodeParameter());
1100         }
1101         emitUnknownStatementNodes(node.getUnknownSchemaNodes());
1102         writer.endNode();
1103     }
1104
1105     private StatementDefinition getStatementChecked(final QName nodeType) {
1106         final StatementDefinition ret = extensions.get(nodeType);
1107         Preconditions.checkArgument(ret != null, "Unknown extension %s used during export.",nodeType);
1108         return ret;
1109     }
1110
1111     private void emitWhen(final RevisionAwareXPath revisionAwareXPath) {
1112         if (revisionAwareXPath != null) {
1113             writer.startWhenNode(revisionAwareXPath);
1114             writer.endNode();
1115         }
1116                 // FIXME: BUG-2444: descriptionNode //FIXME: BUG-2444: Optional
1117         // FIXME: BUG-2444: referenceNode //FIXME: BUG-2444: Optional
1118         // FIXME: BUG-2444: writer.endNode();)
1119
1120     }
1121
1122     private void emitRpc(final RpcDefinition rpc) {
1123         writer.startRpcNode(rpc.getQName());
1124         // FIXME: BUG-2444: *(ifFeatureNode )
1125         emitStatusNode(rpc.getStatus());
1126         emitDescriptionNode(rpc.getDescription());
1127         emitReferenceNode(rpc.getReference());
1128
1129         for (final TypeDefinition<?> typedef : rpc.getTypeDefinitions()) {
1130             emitTypedefNode(typedef);
1131         }
1132         for (final GroupingDefinition grouping : rpc.getGroupings()) {
1133             emitGrouping(grouping);
1134         }
1135         emitInput(rpc.getInput());
1136         emitOutput(rpc.getOutput());
1137         emitUnknownStatementNodes(rpc.getUnknownSchemaNodes());
1138         writer.endNode();
1139
1140     }
1141
1142     private void emitInput(@Nullable final ContainerSchemaNode input) {
1143         if (input != null) {
1144             writer.startInputNode();
1145             emitDataNodeContainer(input);
1146             emitUnknownStatementNodes(input.getUnknownSchemaNodes());
1147             writer.endNode();
1148         }
1149
1150     }
1151
1152     private void emitOutput(@Nullable final ContainerSchemaNode input) {
1153         if (input != null) {
1154             writer.startOutputNode();
1155             emitDataNodeContainer(input);
1156             emitUnknownStatementNodes(input.getUnknownSchemaNodes());
1157             writer.endNode();
1158         }
1159
1160     }
1161
1162     private void emitNotificationNode(final NotificationDefinition notification) {
1163         writer.startNotificationNode(notification.getQName());
1164         // FIXME: BUG-2444: *(ifFeatureNode )
1165         emitDocumentedNode(notification);
1166         emitDataNodeContainer(notification);
1167         emitUnknownStatementNodes(notification.getUnknownSchemaNodes());
1168         writer.endNode();
1169
1170     }
1171
1172
1173     //FIXME: Probably should be moved to utils bundle.
1174     private static <T> boolean  isPrefix(final Iterable<T> prefix, final Iterable<T> other) {
1175         final Iterator<T> prefixIt = prefix.iterator();
1176         final Iterator<T> otherIt = other.iterator();
1177         while (prefixIt.hasNext()) {
1178             if (!otherIt.hasNext()) {
1179                 return false;
1180             }
1181             if (!Objects.deepEquals(prefixIt.next(), otherIt.next())) {
1182                 return false;
1183             }
1184         }
1185         return true;
1186     }
1187
1188     private void emitDeviation(final Deviation deviation) {
1189         /*
1190          * FIXME: BUG-2444:  Deviation is not modeled properly and we are loosing lot of
1191          * information in order to export it properly
1192          *
1193          * writer.startDeviationNode(deviation.getTargetPath());
1194          *
1195          * :descriptionNode //:Optional
1196          *
1197          *
1198          * emitReferenceNode(deviation.getReference());
1199          * :(deviateNotSupportedNode :1*(deviateAddNode :deviateReplaceNode
1200          * :deviateDeleteNode)) :writer.endNode();
1201          */
1202     }
1203 }