/*
* 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.Nonnull;
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.ChoiceSchemaNode;
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.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
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.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 ChoiceSchemaNode) {
emitChoice((ChoiceSchemaNode) 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 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 static 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 static 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.getRawRegularExpression());
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.getIdentity().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.WithStatus 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 )
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 ChoiceSchemaNode 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 ChoiceSchemaNode) {
emitRefineChoiceNodes((ChoiceSchemaNode) 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 static <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 ChoiceSchemaNode value) {
final ChoiceSchemaNode 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) {
if (!unknonwnNode.isAddedByAugmentation() && !unknonwnNode.isAddedByUses()) {
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(@Nonnull final ContainerSchemaNode input) {
if (isExplicitStatement(input)) {
writer.startInputNode();
emitDataNodeContainer(input);
emitUnknownStatementNodes(input.getUnknownSchemaNodes());
writer.endNode();
}
}
private void emitOutput(@Nonnull final ContainerSchemaNode output) {
if (isExplicitStatement(output)) {
writer.startOutputNode();
emitDataNodeContainer(output);
emitUnknownStatementNodes(output.getUnknownSchemaNodes());
writer.endNode();
}
}
private static boolean isExplicitStatement(final ContainerSchemaNode node) {
return node instanceof EffectiveStatement
&& ((EffectiveStatement) node).getDeclared().getStatementSource() == StatementSource.DECLARATION;
}
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();
*/
}
}