+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.export;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.primitives.UnsignedInteger;
+import java.net.URI;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.ExtendedType;
+import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
+
+@Beta
+@NotThreadSafe
+class SchemaContextEmitter {
+
+ private final Rfc6020ModuleWriter writer;
+ private final boolean emitInstantiated;
+ private final boolean emitUses;
+ private final Map<QName, StatementDefinition> extensions;
+
+ SchemaContextEmitter(final Rfc6020ModuleWriter writer, final Map<QName, StatementDefinition> extensions) {
+ this(writer, extensions,false, true);
+ }
+
+ SchemaContextEmitter(final Rfc6020ModuleWriter writer, final Map<QName, StatementDefinition> extensions, final boolean emitInstantiated, final boolean emitUses) {
+ this.writer = Preconditions.checkNotNull(writer);
+ this.emitInstantiated = emitInstantiated;
+ this.emitUses = emitUses;
+ this.extensions = Preconditions.checkNotNull(extensions);
+ }
+
+ static void writeToStatementWriter(final Module module, final SchemaContext ctx, final StatementTextWriter statementWriter) {
+ final Rfc6020ModuleWriter yangSchemaWriter = SchemaToStatementWriterAdaptor.from(statementWriter);
+ final Map<QName, StatementDefinition> extensions = ExtensionStatement.mapFrom(ctx.getExtensions());
+ new SchemaContextEmitter(yangSchemaWriter,extensions).emitModule(module);
+ }
+
+ void emitModule(final Module input) {
+ writer.startModuleNode(input.getName());
+ emitModuleHeader(input);
+ emitLinkageNodes(input);
+ emitMetaNodes(input);
+ emitRevisionNodes(input);
+ emitBodyNodes(input);
+ writer.endNode();
+ }
+
+ private void emitModuleHeader(final Module input) {
+ emitYangVersionNode(input.getYangVersion());
+ emitNamespace(input.getNamespace());
+ emitPrefixNode(input.getPrefix());
+ }
+
+ @SuppressWarnings("unused")
+ private void emitSubmodule(final String input) {
+ /*
+ * FIXME: BUG-2444: Implement submodule export
+ *
+ * submoduleHeaderNodes linkageNodes metaNodes revisionNodes bodyNodes
+ * writer.endNode();
+ */
+ }
+
+ @SuppressWarnings("unused")
+ private void emitSubmoduleHeaderNodes(final Module input) {
+ /*
+ * FIXME: BUG-2444: Implement submodule headers properly
+ *
+ * :yangVersionNode //Optional
+ *
+ * :belongsToNode
+ */
+ }
+
+ private void emitMetaNodes(final Module input) {
+
+ emitOrganizationNode(input.getOrganization()); // FIXME: BUG-2444: Optional
+ emitContact(input.getContact()); // FIXME: BUG-2444: Optional
+ emitDescriptionNode(input.getDescription());
+ emitReferenceNode(input.getReference());
+ }
+
+ private void emitLinkageNodes(final Module input) {
+ for (final ModuleImport importNode : input.getImports()) {
+ emitImport(importNode);
+ }
+ /*
+ * FIXME: BUG-2444: Emit include statements
+ */
+ }
+
+ private void emitRevisionNodes(final Module input) {
+ /*
+ * FIXME: BUG-2444: emit revisions properly, when parsed model will provide enough
+ * information
+ */
+ emitRevision(input.getRevision());
+
+ }
+
+ private void emitBodyNodes(final Module input) {
+
+ for (final ExtensionDefinition extension : input.getExtensionSchemaNodes()) {
+ emitExtension(extension);
+ }
+ for (final FeatureDefinition definition : input.getFeatures()) {
+ emitFeature(definition);
+ }
+ for (final IdentitySchemaNode identity : input.getIdentities()) {
+ emitIdentity(identity);
+ }
+
+ emitDataNodeContainer(input);
+
+ for (final AugmentationSchema augmentation : input.getAugmentations()) {
+ emitAugment(augmentation);
+ }
+ for (final RpcDefinition rpc : input.getRpcs()) {
+ emitRpc(rpc);
+ }
+ for (final NotificationDefinition notification : input.getNotifications()) {
+ emitNotificationNode(notification);
+ }
+ for (final Deviation deviation : input.getDeviations()) {
+ emitDeviation(deviation);
+ }
+
+ }
+
+ private void emitDataNodeContainer(final DataNodeContainer input) {
+ for (final TypeDefinition<?> typedef : input.getTypeDefinitions()) {
+ emitTypedefNode(typedef);
+ }
+ for (final GroupingDefinition grouping : input.getGroupings()) {
+ emitGrouping(grouping);
+ }
+ for (final DataSchemaNode child : input.getChildNodes()) {
+ emitDataSchemaNode(child);
+ }
+ for (final UsesNode usesNode : input.getUses()) {
+ emitUsesNode(usesNode);
+ }
+ }
+
+ private void emitDataSchemaNode(final DataSchemaNode child) {
+ if (!emitInstantiated && (child.isAddedByUses() || child.isAugmenting())) {
+ // We skip instantiated nodes.
+ return;
+ }
+
+ if (child instanceof ContainerSchemaNode) {
+ emitContainer((ContainerSchemaNode) child);
+ } else if (child instanceof LeafSchemaNode) {
+ emitLeaf((LeafSchemaNode) child);
+ } else if (child instanceof LeafListSchemaNode) {
+ emitLeafList((LeafListSchemaNode) child);
+ } else if (child instanceof ListSchemaNode) {
+ emitList((ListSchemaNode) child);
+ } else if (child instanceof ChoiceNode) {
+ emitChoice((ChoiceNode) child);
+ } else if (child instanceof AnyXmlSchemaNode) {
+ emitAnyxml((AnyXmlSchemaNode) child);
+ } else {
+ throw new UnsupportedOperationException("Not supported DataSchemaNode type " + child.getClass());
+ }
+ }
+
+ private void emitYangVersionNode(final String input) {
+ writer.startYangVersionNode(input);
+ writer.endNode();
+ }
+
+ private void emitImport(final ModuleImport importNode) {
+ writer.startImportNode(importNode.getModuleName());
+ emitPrefixNode(importNode.getPrefix());
+ emitRevisionDateNode(importNode.getRevision());
+ writer.endNode();
+ }
+
+ @SuppressWarnings("unused")
+ private void emitInclude(final String input) {
+ /*
+ * FIXME: BUG-2444: Implement proper export of include statements
+ * startIncludeNode(IdentifierHelper.getIdentifier(String :input));
+ *
+ *
+ * :revisionDateNode :writer.endNode();)
+ */
+ }
+
+ private void emitNamespace(final URI uri) {
+ writer.startNamespaceNode(uri);
+ writer.endNode();
+
+ }
+
+ private void emitPrefixNode(final String input) {
+ writer.startPrefixNode(input);
+ writer.endNode();
+
+ }
+
+ @SuppressWarnings("unused")
+ private void emitBelongsTo(final String input) {
+ /*
+ * FIXME: BUG-2444: Implement proper export of belongs-to statements
+ * startIncludeNode(IdentifierHelper.getIdentifier(String :input));
+ *
+ *
+ * :writer.startBelongsToNode(IdentifierHelper.getIdentifier(String
+ * :input));
+ *
+ *
+ * :prefixNode
+ * :writer.endNode();
+ *
+ */
+
+ }
+
+ private void emitOrganizationNode(final String input) {
+ writer.startOrganizationNode(input);
+ writer.endNode();
+
+ }
+
+ private void emitContact(final String input) {
+ writer.startContactNode(input);
+ writer.endNode();
+
+ }
+
+ private void emitDescriptionNode(@Nullable final String input) {
+ if (!Strings.isNullOrEmpty(input)) {
+ writer.startDescriptionNode(input);
+ writer.endNode();
+ }
+ }
+
+ private void emitReferenceNode(@Nullable final String input) {
+ if (!Strings.isNullOrEmpty(input)) {
+ writer.startReferenceNode(input);
+ writer.endNode();
+ }
+ }
+
+ private void emitUnitsNode(@Nullable final String input) {
+ if (!Strings.isNullOrEmpty(input)) {
+ writer.startUnitsNode(input);
+ writer.endNode();
+ }
+ }
+
+ private void emitRevision(final Date date) {
+ writer.startRevisionNode(date);
+
+ //
+ // FIXME: BUG-2444: FIXME: BUG-2444: BUG-2417: descriptionNode //FIXME: BUG-2444: Optional
+ // FIXME: BUG-2444: FIXME: BUG-2444: BUG-2417: referenceNode //FIXME: BUG-2444: Optional
+ writer.endNode();
+
+ }
+
+ private void emitRevisionDateNode(@Nullable final Date date) {
+ if (date != null) {
+ writer.startRevisionDateNode(date);
+ writer.endNode();
+ }
+ }
+
+ private void emitExtension(final ExtensionDefinition extension) {
+ writer.startExtensionNode(extension.getQName());
+ emitArgument(extension.getArgument(),extension.isYinElement());
+ emitStatusNode(extension.getStatus());
+ emitDescriptionNode(extension.getDescription());
+ emitReferenceNode(extension.getReference());
+ emitUnknownStatementNodes(extension.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitArgument(final @Nullable String input, final boolean yinElement) {
+ if (input != null) {
+ writer.startArgumentNode(input);
+ emitYinElement(yinElement);
+ writer.endNode();
+ }
+
+ }
+
+ private void emitYinElement(final boolean yinElement) {
+ writer.startYinElementNode(yinElement);
+ writer.endNode();
+
+ }
+
+ private void emitIdentity(final IdentitySchemaNode identity) {
+ writer.startIdentityNode(identity.getQName());
+ if (identity.getBaseIdentity() != null) {
+ emitBase(identity.getBaseIdentity().getQName());
+ }
+ emitStatusNode(identity.getStatus());
+ emitDescriptionNode(identity.getDescription());
+ emitReferenceNode(identity.getReference());
+ writer.endNode();
+
+ }
+
+ private void emitBase(final QName qName) {
+ writer.startBaseNode(qName);
+ writer.endNode();
+
+ }
+
+ private void emitFeature(final FeatureDefinition definition) {
+ writer.startFeatureNode(definition.getQName());
+
+ // FIXME: BUG-2444: FIXME: BUG-2444: Expose ifFeature *(ifFeatureNode )
+ emitStatusNode(definition.getStatus());
+ emitDescriptionNode(definition.getDescription());
+ emitReferenceNode(definition.getReference());
+ writer.endNode();
+
+ }
+
+ @SuppressWarnings("unused")
+ private void emitIfFeature(final String input) {
+ /*
+ * FIXME: BUG-2444: Implement proper export of include statements
+ * startIncludeNode(IdentifierHelper.getIdentifier(String :input));
+ *
+ */
+ }
+
+ private void emitTypedefNode(final TypeDefinition<?> typedef) {
+ writer.startTypedefNode(typedef.getQName());
+ // Differentiate between derived type and existing type
+ // name.
+ emitTypeNodeDerived(typedef);
+ emitUnitsNode(typedef.getUnits());
+ emitDefaultNode(typedef.getDefaultValue());
+ emitStatusNode(typedef.getStatus());
+ emitDescriptionNode(typedef.getDescription());
+ emitReferenceNode(typedef.getReference());
+ emitUnknownStatementNodes(typedef.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitTypeNode(final SchemaPath parentPath, final TypeDefinition<?> subtype) {
+ final SchemaPath path = subtype.getPath();
+ if (isPrefix(parentPath.getPathFromRoot(), path.getPathFromRoot())) {
+ emitTypeNodeDerived(subtype);
+ } else {
+ emitTypeNodeReferenced(subtype);
+ }
+ }
+
+ private void emitTypeNodeReferenced(final TypeDefinition<?> typeDefinition) {
+ writer.startTypeNode(typeDefinition.getQName());
+ writer.endNode();
+
+ }
+
+ private void emitTypeNodeDerived(final TypeDefinition<?> typeDefinition) {
+ final TypeDefinition<?> baseType;
+ if (typeDefinition.getBaseType() != null) {
+ baseType = typeDefinition.getBaseType();
+ } else {
+ baseType = typeDefinition;
+ }
+ writer.startTypeNode(baseType.getQName());
+ emitTypeBodyNodes(typeDefinition);
+ writer.endNode();
+
+ }
+
+ private void emitTypeBodyNodes(final TypeDefinition<?> typeDef) {
+ if (typeDef instanceof ExtendedType) {
+ emitTypeBodyNodes(NormalizatedDerivedType.from((ExtendedType) typeDef));
+ } else if (typeDef instanceof UnsignedIntegerTypeDefinition) {
+ emitUnsignedIntegerSpecification((UnsignedIntegerTypeDefinition) typeDef);
+ } else if (typeDef instanceof IntegerTypeDefinition) {
+ emitIntegerSpefication((IntegerTypeDefinition) typeDef);
+ } else if (typeDef instanceof DecimalTypeDefinition) {
+ emitDecimal64Specification((DecimalTypeDefinition) typeDef);
+ } else if (typeDef instanceof StringTypeDefinition) {
+ emitStringRestrictions((StringTypeDefinition) typeDef);
+ } else if (typeDef instanceof EnumTypeDefinition) {
+ emitEnumSpecification((EnumTypeDefinition) typeDef);
+ } else if (typeDef instanceof LeafrefTypeDefinition) {
+ emitLeafrefSpecification((LeafrefTypeDefinition) typeDef);
+ } else if (typeDef instanceof IdentityrefTypeDefinition) {
+ emitIdentityrefSpecification((IdentityrefTypeDefinition) typeDef);
+ } else if (typeDef instanceof InstanceIdentifierTypeDefinition) {
+ emitInstanceIdentifierSpecification((InstanceIdentifierTypeDefinition) typeDef);
+ } else if (typeDef instanceof BitsTypeDefinition) {
+ emitBitsSpecification((BitsTypeDefinition) typeDef);
+ } else if (typeDef instanceof UnionTypeDefinition) {
+ emitUnionSpecification((UnionTypeDefinition) typeDef);
+ } else if (typeDef instanceof BinaryTypeDefinition) {
+ // FIXME: BUG-2444: Is this realy NOOP?
+ // should at least support length statement
+ } else if (typeDef instanceof BooleanTypeDefinition || typeDef instanceof EmptyTypeDefinition) {
+ // NOOP
+ } else {
+ throw new IllegalArgumentException("Not supported type " + typeDef.getClass());
+ }
+ }
+
+ private void emitIntegerSpefication(final IntegerTypeDefinition typeDef) {
+ emitRangeNodeOptional(typeDef.getRangeConstraints());
+ }
+
+ private void emitUnsignedIntegerSpecification(final UnsignedIntegerTypeDefinition typeDef) {
+ emitRangeNodeOptional(typeDef.getRangeConstraints());
+
+ }
+
+ private void emitRangeNodeOptional(final List<RangeConstraint> list) {
+ // FIXME: BUG-2444: Wrong decomposition in API, should be LenghtConstraint
+ // which contains ranges.
+ if (!list.isEmpty()) {
+ writer.startRangeNode(toRangeString(list));
+ final RangeConstraint first = list.iterator().next();
+ emitErrorMessageNode(first.getErrorMessage());
+ emitErrorAppTagNode(first.getErrorAppTag());
+ emitDescriptionNode(first.getDescription());
+ emitReferenceNode(first.getReference());
+ writer.endNode();
+ }
+
+ }
+
+ private void emitDecimal64Specification(final DecimalTypeDefinition typeDefinition) {
+ emitFranctionDigitsNode(typeDefinition.getFractionDigits());
+ emitRangeNodeOptional(typeDefinition.getRangeConstraints());
+
+ }
+
+ private void emitFranctionDigitsNode(final Integer fractionDigits) {
+ writer.startFractionDigitsNode(fractionDigits);
+ writer.endNode();
+ }
+
+ private void emitStringRestrictions(final StringTypeDefinition typeDef) {
+
+ // FIXME: BUG-2444: Wrong decomposition in API, should be LenghtConstraint
+ // which contains ranges.
+ emitLength(typeDef.getLengthConstraints());
+
+ for (final PatternConstraint pattern : typeDef.getPatternConstraints()) {
+ emitPatternNode(pattern);
+ }
+
+ }
+
+ private void emitLength(final List<LengthConstraint> list) {
+ if (!list.isEmpty()) {
+ writer.startLengthNode(toLengthString(list));
+ // FIXME: BUG-2444: Workaround for incorrect decomposition in API
+ final LengthConstraint first = list.iterator().next();
+ emitErrorMessageNode(first.getErrorMessage());
+ emitErrorAppTagNode(first.getErrorAppTag());
+ emitDescriptionNode(first.getDescription());
+ emitReferenceNode(first.getReference());
+ writer.endNode();
+ }
+
+ }
+
+ private String toLengthString(final List<LengthConstraint> list) {
+ final StringBuilder lengthStr = new StringBuilder();
+ final Iterator<LengthConstraint> constIt = list.iterator();
+ while (constIt.hasNext()) {
+ final LengthConstraint current = constIt.next();
+ if (current.getMin() == current.getMax()) {
+ lengthStr.append(current.getMin());
+ } else {
+ lengthStr.append(current.getMin());
+ lengthStr.append("..");
+ lengthStr.append(current.getMax());
+ }
+ if (constIt.hasNext()) {
+ lengthStr.append("|");
+ }
+ }
+ return lengthStr.toString();
+ }
+
+ private String toRangeString(final List<RangeConstraint> list) {
+ final StringBuilder lengthStr = new StringBuilder();
+ final Iterator<RangeConstraint> constIt = list.iterator();
+ while (constIt.hasNext()) {
+ final RangeConstraint current = constIt.next();
+ if (current.getMin() == current.getMax()) {
+ lengthStr.append(current.getMin());
+ } else {
+ lengthStr.append(current.getMin());
+ lengthStr.append("..");
+ lengthStr.append(current.getMax());
+ }
+ if (constIt.hasNext()) {
+ lengthStr.append("|");
+ }
+ }
+ return lengthStr.toString();
+ }
+
+ private void emitPatternNode(final PatternConstraint pattern) {
+ writer.startPatternNode(pattern.getRegularExpression());
+ emitErrorMessageNode(pattern.getErrorMessage()); // FIXME: BUG-2444: Optional
+ emitErrorAppTagNode(pattern.getErrorAppTag()); // FIXME: BUG-2444: Optional
+ emitDescriptionNode(pattern.getDescription());
+ emitReferenceNode(pattern.getReference()); // FIXME: BUG-2444: Optional
+ writer.endNode();
+ }
+
+ private void emitDefaultNode(@Nullable final Object object) {
+ if (object != null) {
+ writer.startDefaultNode(object.toString());
+ writer.endNode();
+ }
+
+ }
+
+ private void emitEnumSpecification(final EnumTypeDefinition typeDefinition) {
+ for (final EnumPair enumValue : typeDefinition.getValues()) {
+ emitEnumNode(enumValue);
+ }
+ }
+
+ private void emitEnumNode(final EnumPair enumValue) {
+ writer.startEnumNode(enumValue.getName());
+ emitValueNode(enumValue.getValue());
+ emitStatusNode(enumValue.getStatus());
+ emitDescriptionNode(enumValue.getDescription());
+ emitReferenceNode(enumValue.getReference());
+ writer.endNode();
+ }
+
+ private void emitLeafrefSpecification(final LeafrefTypeDefinition typeDefinition) {
+ emitPathNode(typeDefinition.getPathStatement());
+ // FIXME: BUG-2444: requireInstanceNode /Optional removed with (RFC6020 - Errata ID
+ // 2949)
+ // Added in Yang 1.1
+ }
+
+ private void emitPathNode(final RevisionAwareXPath revisionAwareXPath) {
+ writer.startPathNode(revisionAwareXPath);
+ writer.endNode();
+ }
+
+ private void emitRequireInstanceNode(final boolean require) {
+ writer.startRequireInstanceNode(require);
+ writer.endNode();
+ }
+
+ private void emitInstanceIdentifierSpecification(final InstanceIdentifierTypeDefinition typeDefinition) {
+ emitRequireInstanceNode(typeDefinition.requireInstance());
+ }
+
+ private void emitIdentityrefSpecification(final IdentityrefTypeDefinition typeDefinition) {
+ emitBase(typeDefinition.getQName());
+ }
+
+ private void emitUnionSpecification(final UnionTypeDefinition typeDefinition) {
+ for (final TypeDefinition<?> subtype : typeDefinition.getTypes()) {
+ // FIXME: BUG-2444: What if we have locally modified types here?
+ // is solution to look-up in schema path?
+ emitTypeNode(typeDefinition.getPath(), subtype);
+ }
+ }
+
+ private void emitBitsSpecification(final BitsTypeDefinition typeDefinition) {
+ for (final Bit bit : typeDefinition.getBits()) {
+ emitBit(bit);
+ }
+ }
+
+ private void emitBit(final Bit bit) {
+ writer.startBitNode(bit.getName());
+ emitPositionNode(bit.getPosition());
+ emitStatusNode(bit.getStatus());
+ emitDescriptionNode(bit.getDescription());
+ emitReferenceNode(bit.getReference());
+ writer.endNode();
+ }
+
+ private void emitPositionNode(@Nullable final Long position) {
+ if (position != null) {
+ writer.startPositionNode(UnsignedInteger.valueOf(position));
+ writer.endNode();
+ }
+ }
+
+ private void emitStatusNode(@Nullable final Status status) {
+ if (status != null) {
+ writer.startStatusNode(status);
+ writer.endNode();
+ }
+ }
+
+ private void emitConfigNode(final boolean config) {
+ writer.startConfigNode(config);
+ writer.endNode();
+ }
+
+ private void emitMandatoryNode(final boolean mandatory) {
+ writer.startMandatoryNode(mandatory);
+ writer.endNode();
+ }
+
+ private void emitPresenceNode(final boolean presence) {
+ writer.startPresenceNode(presence);
+ writer.endNode();
+ }
+
+ private void emitOrderedBy(final boolean userOrdered) {
+ if (userOrdered) {
+ writer.startOrderedByNode("user");
+ } else {
+ writer.startOrderedByNode("system");
+ }
+ writer.endNode();
+ }
+
+ private void emitMust(@Nullable final MustDefinition mustCondition) {
+ if(mustCondition != null && mustCondition.getXpath() != null) {
+ writer.startMustNode(mustCondition.getXpath());
+ emitErrorMessageNode(mustCondition.getErrorMessage());
+ emitErrorAppTagNode(mustCondition.getErrorAppTag());
+ emitDescriptionNode(mustCondition.getDescription());
+ emitReferenceNode(mustCondition.getReference());
+ writer.endNode();
+ }
+
+ }
+
+ private void emitErrorMessageNode(@Nullable final String input) {
+ if (input != null && !input.isEmpty()) {
+ writer.startErrorMessageNode(input);
+ writer.endNode();
+ }
+ }
+
+ private void emitErrorAppTagNode(final String input) {
+ if (input != null && !input.isEmpty()) {
+ writer.startErrorAppTagNode(input);
+ writer.endNode();
+ }
+ }
+
+ private void emitMinElementsNode(final Integer min) {
+ if (min != null) {
+ writer.startMinElementsNode(min);
+ writer.endNode();
+ }
+ }
+
+ private void emitMaxElementsNode(final Integer max) {
+ if (max != null) {
+ writer.startMaxElementsNode(max);
+ writer.endNode();
+ }
+ }
+
+ private void emitValueNode(@Nullable final Integer value) {
+ if (value != null) {
+ writer.startValueNode(value);
+ writer.endNode();
+ }
+ }
+
+ private void emitDocumentedNode(final DocumentedNode input) {
+ emitStatusNode(input.getStatus());
+ emitDescriptionNode(input.getDescription());
+ emitReferenceNode(input.getReference());
+ }
+
+ private void emitGrouping(final GroupingDefinition grouping) {
+ writer.startGroupingNode(grouping.getQName());
+ emitDocumentedNode(grouping);
+ emitDataNodeContainer(grouping);
+ emitUnknownStatementNodes(grouping.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitContainer(final ContainerSchemaNode child) {
+ writer.startContainerNode(child.getQName());
+
+ //
+
+ emitConstraints(child.getConstraints());
+ // FIXME: BUG-2444: whenNode //:Optional
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitMustNodes(child.getConstraints().getMustConstraints());
+ emitPresenceNode(child.isPresenceContainer());
+ emitConfigNode(child.isConfiguration());
+ emitDocumentedNode(child);
+ emitDataNodeContainer(child);
+ emitUnknownStatementNodes(child.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitConstraints(final ConstraintDefinition constraints) {
+ emitWhen(constraints.getWhenCondition());
+ for (final MustDefinition mustCondition : constraints.getMustConstraints()) {
+ emitMust(mustCondition);
+ }
+
+ }
+
+ private void emitLeaf(final LeafSchemaNode child) {
+ writer.startLeafNode(child.getQName());
+ emitWhen(child.getConstraints().getWhenCondition());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitTypeNode(child.getPath(), child.getType());
+ emitUnitsNode(child.getUnits());
+ emitMustNodes(child.getConstraints().getMustConstraints());
+ emitDefaultNode(child.getDefault());
+ emitConfigNode(child.isConfiguration());
+ emitMandatoryNode(child.getConstraints().isMandatory());
+ emitDocumentedNode(child);
+ emitUnknownStatementNodes(child.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitLeafList(final LeafListSchemaNode child) {
+ writer.startLeafListNode(child.getQName());
+
+ emitWhen(child.getConstraints().getWhenCondition());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitTypeNode(child.getPath(), child.getType());
+ // FIXME: BUG-2444: unitsNode /Optional
+ emitMustNodes(child.getConstraints().getMustConstraints());
+ emitConfigNode(child.isConfiguration());
+
+ emitMinElementsNode(child.getConstraints().getMinElements());
+ emitMaxElementsNode(child.getConstraints().getMaxElements());
+ emitOrderedBy(child.isUserOrdered());
+ emitDocumentedNode(child);
+ emitUnknownStatementNodes(child.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitList(final ListSchemaNode child) {
+ writer.startListNode(child.getQName());
+ emitWhen(child.getConstraints().getWhenCondition());
+
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitMustNodes(child.getConstraints().getMustConstraints());
+ emitKey(child.getKeyDefinition());
+ // FIXME: BUG-2444: *(uniqueNode )
+ emitConfigNode(child.isConfiguration());
+ emitMinElementsNode(child.getConstraints().getMinElements());
+ emitMaxElementsNode(child.getConstraints().getMaxElements());
+ emitOrderedBy(child.isUserOrdered());
+ emitDocumentedNode(child);
+ emitDataNodeContainer(child);
+ emitUnknownStatementNodes(child.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitMustNodes(final Set<MustDefinition> mustConstraints) {
+ for (final MustDefinition must : mustConstraints) {
+ emitMust(must);
+ }
+ }
+
+ private void emitKey(final List<QName> keyList) {
+ if (keyList != null && !keyList.isEmpty()) {
+ writer.startKeyNode(keyList);
+ writer.endNode();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private void emitUnique(final String input) {
+ // FIXME: BUG-2444: writer.startUniqueNode(uniqueArgStr)); Nodeend
+
+ }
+
+ private void emitChoice(final ChoiceNode choice) {
+ writer.startChoiceNode(choice.getQName());
+ emitWhen(choice.getConstraints().getWhenCondition());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ // FIXME: BUG-2444: defaultNode //Optional
+ emitConfigNode(choice.isConfiguration());
+ emitMandatoryNode(choice.getConstraints().isMandatory());
+ emitDocumentedNode(choice);
+ for (final ChoiceCaseNode caze : choice.getCases()) {
+ // TODO: emit short case?
+ emitCaseNode(caze);
+ }
+ emitUnknownStatementNodes(choice.getUnknownSchemaNodes());
+ writer.endNode();
+ }
+
+ private void emitCaseNode(final ChoiceCaseNode caze) {
+ if (!emitInstantiated && caze.isAugmenting()) {
+ return;
+ }
+ writer.startCaseNode(caze.getQName());
+ emitWhen(caze.getConstraints().getWhenCondition());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitDocumentedNode(caze);
+ emitDataNodeContainer(caze);
+ emitUnknownStatementNodes(caze.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitAnyxml(final AnyXmlSchemaNode child) {
+ writer.startAnyxmlNode(child.getQName());
+
+ emitWhen(child.getConstraints().getWhenCondition());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitMustNodes(child.getConstraints().getMustConstraints());
+ emitConfigNode(child.isConfiguration());
+ emitMandatoryNode(child.getConstraints().isMandatory());
+ emitDocumentedNode(child);
+ emitUnknownStatementNodes(child.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitUsesNode(final UsesNode usesNode) {
+ if (emitUses && !usesNode.isAddedByUses() && !usesNode.isAugmenting()) {
+ writer.startUsesNode(usesNode.getGroupingPath().getLastComponent());
+ /*
+ * FIXME: BUG-2444:
+ * whenNode /
+ * *(ifFeatureNode )
+ * statusNode // Optional F
+ * : descriptionNode // Optional
+ * referenceNode // Optional
+ */
+ for (final Entry<SchemaPath, SchemaNode> refine : usesNode.getRefines().entrySet()) {
+ emitRefine(refine);
+ }
+ for (final AugmentationSchema aug : usesNode.getAugmentations()) {
+ emitUsesAugmentNode(aug);
+ }
+ writer.endNode();
+ }
+ }
+
+ private void emitRefine(final Entry<SchemaPath, SchemaNode> refine) {
+ final SchemaPath path = refine.getKey();
+ final SchemaNode value = refine.getValue();
+ writer.startRefineNode(path);
+
+ if (value instanceof LeafSchemaNode) {
+ emitRefineLeafNodes((LeafSchemaNode) value);
+ } else if (value instanceof LeafListSchemaNode) {
+ emitRefineLeafListNodes((LeafListSchemaNode) value);
+ } else if (value instanceof ListSchemaNode) {
+ emitRefineListNodes((ListSchemaNode) value);
+ } else if (value instanceof ChoiceNode) {
+ emitRefineChoiceNodes((ChoiceNode) value);
+ } else if (value instanceof ChoiceCaseNode) {
+ emitRefineCaseNodes((ChoiceCaseNode) value);
+ } else if (value instanceof ContainerSchemaNode) {
+ emitRefineContainerNodes((ContainerSchemaNode) value);
+ } else if (value instanceof AnyXmlSchemaNode) {
+ emitRefineAnyxmlNodes((AnyXmlSchemaNode) value);
+ }
+ writer.endNode();
+
+ }
+
+ private <T extends SchemaNode> T getOriginalChecked(final T value) {
+ final Optional<SchemaNode> original = SchemaNodeUtils.getOriginalIfPossible(value);
+ Preconditions.checkArgument(original.isPresent(), "Original unmodified version of node is not present.");
+ @SuppressWarnings("unchecked")
+ final T ret = (T) original.get();
+ return ret;
+ }
+
+ private void emitDocumentedNodeRefine(final DocumentedNode original, final DocumentedNode value) {
+ if (Objects.deepEquals(original.getDescription(), value.getDescription())) {
+ emitDescriptionNode(value.getDescription());
+ }
+ if (Objects.deepEquals(original.getReference(), value.getReference())) {
+ emitReferenceNode(value.getReference());
+ }
+ }
+
+ private void emitRefineContainerNodes(final ContainerSchemaNode value) {
+ final ContainerSchemaNode original = getOriginalChecked(value);
+
+ // emitMustNodes(child.getConstraints().getMustConstraints());
+ if (Objects.deepEquals(original.isPresenceContainer(), value.isPresenceContainer())) {
+ emitPresenceNode(value.isPresenceContainer());
+ }
+ if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
+ emitConfigNode(value.isConfiguration());
+ }
+ emitDocumentedNodeRefine(original, value);
+
+ }
+
+ private void emitRefineLeafNodes(final LeafSchemaNode value) {
+ final LeafSchemaNode original = getOriginalChecked(value);
+
+ // emitMustNodes(child.getConstraints().getMustConstraints());
+ if (Objects.deepEquals(original.getDefault(), value.getDefault())) {
+ emitDefaultNode(value.getDefault());
+ }
+ if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
+ emitConfigNode(value.isConfiguration());
+ }
+ emitDocumentedNodeRefine(original, value);
+ if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
+ emitMandatoryNode(value.getConstraints().isMandatory());
+ }
+
+ }
+
+ private void emitRefineLeafListNodes(final LeafListSchemaNode value) {
+ final LeafListSchemaNode original = getOriginalChecked(value);
+
+ // emitMustNodes(child.getConstraints().getMustConstraints());
+ if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
+ emitConfigNode(value.isConfiguration());
+ }
+ if (Objects.deepEquals(original.getConstraints().getMinElements(), value.getConstraints().getMinElements())) {
+ emitMinElementsNode(value.getConstraints().getMinElements());
+ }
+ if (Objects.deepEquals(original.getConstraints().getMaxElements(), value.getConstraints().getMaxElements())) {
+ emitMaxElementsNode(value.getConstraints().getMaxElements());
+ }
+ emitDocumentedNodeRefine(original, value);
+
+ }
+
+ private void emitRefineListNodes(final ListSchemaNode value) {
+ final ListSchemaNode original = getOriginalChecked(value);
+
+ // emitMustNodes(child.getConstraints().getMustConstraints());
+ if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
+ emitConfigNode(value.isConfiguration());
+ }
+ if (Objects.deepEquals(original.getConstraints().getMinElements(), value.getConstraints().getMinElements())) {
+ emitMinElementsNode(value.getConstraints().getMinElements());
+ }
+ if (Objects.deepEquals(original.getConstraints().getMaxElements(), value.getConstraints().getMaxElements())) {
+ emitMaxElementsNode(value.getConstraints().getMaxElements());
+ }
+ emitDocumentedNodeRefine(original, value);
+
+ }
+
+ private void emitRefineChoiceNodes(final ChoiceNode value) {
+ final ChoiceNode original = getOriginalChecked(value);
+
+ // FIXME: BUG-2444: defaultNode //FIXME: BUG-2444: Optional
+ if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
+ emitConfigNode(value.isConfiguration());
+ }
+ if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
+ emitMandatoryNode(value.getConstraints().isMandatory());
+ }
+ emitDocumentedNodeRefine(original, value);
+
+ }
+
+ private void emitRefineCaseNodes(final ChoiceCaseNode value) {
+ final ChoiceCaseNode original = getOriginalChecked(value);
+ emitDocumentedNodeRefine(original, value);
+
+ }
+
+ private void emitRefineAnyxmlNodes(final AnyXmlSchemaNode value) {
+ final AnyXmlSchemaNode original = getOriginalChecked(value);
+
+ // FIXME: BUG-2444: emitMustNodes(child.getConstraints().getMustConstraints());
+ if (Objects.deepEquals(original.isConfiguration(), value.isConfiguration())) {
+ emitConfigNode(value.isConfiguration());
+ }
+ if (Objects.deepEquals(original.getConstraints().isMandatory(), value.getConstraints().isMandatory())) {
+ emitMandatoryNode(value.getConstraints().isMandatory());
+ }
+ emitDocumentedNodeRefine(original, value);
+
+ }
+
+ private void emitUsesAugmentNode(final AugmentationSchema aug) {
+ /**
+ * differs only in location in schema, otherwise currently (as of
+ * RFC6020) it is same, so we could freely reuse path.
+ */
+ emitAugment(aug);
+ }
+
+ private void emitAugment(final AugmentationSchema augmentation) {
+ writer.startAugmentNode(augmentation.getTargetPath());
+ // FIXME: BUG-2444: whenNode //Optional
+ // FIXME: BUG-2444: *(ifFeatureNode )
+
+ emitStatusNode(augmentation.getStatus());
+ emitDescriptionNode(augmentation.getDescription());
+ emitReferenceNode(augmentation.getReference());
+ for(final UsesNode uses: augmentation.getUses()) {
+ emitUsesNode(uses);
+ }
+
+ for (final DataSchemaNode childNode : augmentation.getChildNodes()) {
+ if (childNode instanceof ChoiceCaseNode) {
+ emitCaseNode((ChoiceCaseNode) childNode);
+ } else {
+ emitDataSchemaNode(childNode);
+ }
+ }
+ emitUnknownStatementNodes(augmentation.getUnknownSchemaNodes());
+ writer.endNode();
+ }
+
+ private void emitUnknownStatementNodes(final List<UnknownSchemaNode> unknownNodes) {
+ for (final UnknownSchemaNode unknonwnNode : unknownNodes) {
+ emitUnknownStatementNode(unknonwnNode);
+ }
+ }
+
+ private void emitUnknownStatementNode(final UnknownSchemaNode node) {
+ final StatementDefinition def = getStatementChecked(node.getNodeType());
+ if (def.getArgumentName() == null) {
+ writer.startUnknownNode(def);
+ } else {
+ writer.startUnknownNode(def, node.getNodeParameter());
+ }
+ emitUnknownStatementNodes(node.getUnknownSchemaNodes());
+ writer.endNode();
+ }
+
+ private StatementDefinition getStatementChecked(final QName nodeType) {
+ final StatementDefinition ret = extensions.get(nodeType);
+ Preconditions.checkArgument(ret != null, "Unknown extension %s used during export.",nodeType);
+ return ret;
+ }
+
+ private void emitWhen(final RevisionAwareXPath revisionAwareXPath) {
+ if(revisionAwareXPath != null) {
+ writer.startWhenNode(revisionAwareXPath);
+ writer.endNode();
+ }
+ // FIXME: BUG-2444: descriptionNode //FIXME: BUG-2444: Optional
+ // FIXME: BUG-2444: referenceNode //FIXME: BUG-2444: Optional
+ // FIXME: BUG-2444: writer.endNode();)
+
+ }
+
+ private void emitRpc(final RpcDefinition rpc) {
+ writer.startRpcNode(rpc.getQName());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitStatusNode(rpc.getStatus());
+ emitDescriptionNode(rpc.getDescription());
+ emitReferenceNode(rpc.getReference());
+
+ for(final TypeDefinition<?> typedef : rpc.getTypeDefinitions()) {
+ emitTypedefNode(typedef);
+ }
+ for(final GroupingDefinition grouping : rpc.getGroupings()) {
+ emitGrouping(grouping);
+ }
+ emitInput(rpc.getInput());
+ emitOutput(rpc.getOutput());
+ emitUnknownStatementNodes(rpc.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+ private void emitInput(@Nullable final ContainerSchemaNode input) {
+ if (input != null) {
+ writer.startInputNode();
+ emitDataNodeContainer(input);
+ emitUnknownStatementNodes(input.getUnknownSchemaNodes());
+ writer.endNode();
+ }
+
+ }
+
+ private void emitOutput(@Nullable final ContainerSchemaNode input) {
+ if (input != null) {
+ writer.startOutputNode();
+ emitDataNodeContainer(input);
+ emitUnknownStatementNodes(input.getUnknownSchemaNodes());
+ writer.endNode();
+ }
+
+ }
+
+ private void emitNotificationNode(final NotificationDefinition notification) {
+ writer.startNotificationNode(notification.getQName());
+ // FIXME: BUG-2444: *(ifFeatureNode )
+ emitDocumentedNode(notification);
+ emitDataNodeContainer(notification);
+ emitUnknownStatementNodes(notification.getUnknownSchemaNodes());
+ writer.endNode();
+
+ }
+
+
+ //FIXME: Probably should be moved to utils bundle.
+ private static <T> boolean isPrefix(final Iterable<T> prefix, final Iterable<T> other) {
+ final Iterator<T> prefixIt = prefix.iterator();
+ final Iterator<T> otherIt = other.iterator();
+ while(prefixIt.hasNext()) {
+ if(!otherIt.hasNext()) {
+ return false;
+ }
+ if(!Objects.deepEquals(prefixIt.next(), otherIt.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void emitDeviation(final Deviation deviation) {
+ /*
+ * FIXME: BUG-2444: Deviation is not modeled properly and we are loosing lot of
+ * information in order to export it properly
+ *
+ * writer.startDeviationNode(deviation.getTargetPath());
+ *
+ * :descriptionNode //:Optional
+ *
+ *
+ * emitReferenceNode(deviation.getReference());
+ * :(deviateNotSupportedNode :1*(deviateAddNode :deviateReplaceNode
+ * :deviateDeleteNode)) :writer.endNode();
+ */
+ }
+}