genCtx.get(module).addChildNodeType(node, genType);
groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings());
processUsesAugments((DataNodeContainer) node, module);
- if (node.isAddedByUses() || node.isAugmenting())
- genType.setSuitableForBoxing(false);
}
return genType;
}
- private boolean hasWhenOrMustConstraints(final SchemaNode node) {
- boolean hasWhenCondition;
- boolean hasMustConstraints;
-
- if (node instanceof ContainerSchemaNode) {
- ContainerSchemaNode contNode = (ContainerSchemaNode)node;
- hasWhenCondition = contNode.getConstraints().getWhenCondition() != null;
- hasMustConstraints = !isNullOrEmpty(contNode.getConstraints().getMustConstraints());
-
- if (hasWhenCondition || hasMustConstraints)
- return true;
- }
- return false;
- }
-
private void containerToGenType(final Module module, final String basePackageName,
final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ContainerSchemaNode node) {
final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node);
-
if (genType != null) {
constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), genType);
resolveDataSchemaNodes(module, basePackageName, genType, genType, node.getChildNodes());
-
- final String parentName = parent.getName();
- final String childOfName = childOf.getName();
-
- if (parent != null && !parent.getName().contains("Data"))
- genType.setParentType(parent);
- genType.setSuitableForBoxing(hasOnlyOneChild(node) && !hasWhenOrMustConstraints(node));
-
- if (parentName.equals(childOfName))
- genType.setSuitableForBoxing(false);
}
}
- private boolean hasOnlyOneChild(final ContainerSchemaNode contNode) {
- if (!isNullOrEmpty(contNode.getChildNodes()) && contNode.getChildNodes().size() == 1)
- return true;
- return false;
- }
-
private void listToGenType(final Module module, final String basePackageName, final GeneratedTypeBuilder parent,
final GeneratedTypeBuilder childOf, final ListSchemaNode node) {
final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node);
-
if (genType != null) {
- if (!parent.getName().equals(childOf) && !parent.getName().contains("Data")) {
- genType.setParentType(parent);
- }
constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), Types.listTypeFor(genType));
final List<String> listKeys = listKeys(node);
if (targetTypeBuilder == null) {
throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
}
- targetTypeBuilder.setSuitableForBoxing(false);
if (!(targetSchemaNode instanceof ChoiceNode)) {
String packageName = augmentPackageName;
constructGetter(parent, choiceNode.getQName().getLocalName(), choiceNode.getDescription(),
choiceTypeBuilder);
choiceTypeBuilder.addImplementsType(typeForClass(DataContainer.class));
- choiceTypeBuilder.setParentType(parent);
genCtx.get(module).addChildNodeType(choiceNode, choiceTypeBuilder);
generateTypesFromChoiceCases(module, basePackageName, choiceTypeBuilder.toInstance(), choiceNode);
}
return values;
}
- @Override
- public boolean isSuitableForBoxing() {
- return false;
- }
-
@Override
public List<AnnotationType> getAnnotations() {
return annotations;
public String getModuleName() {
return moduleName;
}
-
}
}
public String getModuleName() {
return moduleName;
}
-
- @Override
- public boolean isSuitableForBoxing() {
- return false;
- }
}
}
package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType;
-import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
import org.opendaylight.yangtools.yang.common.QName;
private String reference;
private String moduleName;
private Iterable<QName> schemaPath;
- private boolean isSuitableForBoxing;
- private GeneratedTypeBuilder parentType;
public GeneratedTypeBuilderImpl(final String packageName, final String name) {
super(packageName, name);
this.reference = reference;
}
- @Override
- public void setSuitableForBoxing(boolean value) {
- this.isSuitableForBoxing = value;
- }
-
- @Override
- public void setParentType(GeneratedTypeBuilder parent) {
- this.parentType = parent;
- }
-
- @Override
- public Type getParent() {
- return this.parentType;
- }
-
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
private final String reference;
private final String moduleName;
private final Iterable<QName> schemaPath;
- private final boolean isSuitableForBoxing;
- private final GeneratedTypeBuilder parentType;
public GeneratedTypeImpl(GeneratedTypeBuilderImpl builder) {
super(builder);
this.reference = builder.reference;
this.moduleName = builder.moduleName;
this.schemaPath = builder.schemaPath;
- this.isSuitableForBoxing = builder.isSuitableForBoxing;
- this.parentType = builder.parentType;
}
@Override
public String getModuleName() {
return moduleName;
}
-
- @Override
- public boolean isSuitableForBoxing() {
- return isSuitableForBoxing;
- }
}
-
}
import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
import org.opendaylight.yangtools.sal.binding.model.api.Type
import org.opendaylight.yangtools.yang.binding.Augmentable
-import org.opendaylight.yangtools.yang.binding.ChildOf
import org.opendaylight.yangtools.yang.binding.DataObject
import org.opendaylight.yangtools.yang.binding.Identifiable
«generateSetters»
- «generateBuildMethod»
-
- «generateBuildBoxedMethod»
+ public «type.name» build() {
+ return new «type.name»«IMPL»(this);
+ }
private static final class «type.name»«IMPL» implements «type.name» {
}
'''
- def private generateBuildMethod() '''
- public «type.name» build() {
- return new «type.name»«IMPL»(this);
- }
- '''
-
- def private generateBuildBoxedMethod() {
- if(type.suitableForBoxing && type.parentType != null && isContainerAndIsNotList(type)) {
- val parentTypeBuilder = createParentTypeBuilder()
- if (countMatches(parentTypeBuilder, "org") < 2) {
- return '''
- public «type.parentType.importedName» buildBoxed() {
- return new «parentTypeBuilder»().set«type.name»(build()).build();
- }
- '''
- }
- }
- return ''
- }
-
- def private int countMatches(String string, String subString) {
- if (string.nullOrEmpty || subString.nullOrEmpty) {
- return 0
- }
- var int count = 0;
- var int idx = 0;
- while ((idx = string.indexOf(subString, idx)) != -1) {
- count = count + 1;
- idx = idx + subString.length();
- }
- return count;
- }
-
- def private createParentTypeBuilder() {
- return type.parentType.packageName + "." + type.parentType.importedName + "Builder"
- }
-
- def private boolean isContainerAndIsNotList(GeneratedType type) {
- val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))
- val implementsChildOf = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(ChildOf), type))
-
- if (implementsChildOf && !isList) {
- return true
- }
- return false;
- }
-
/**
* Generate default constructor and constructor for every implemented interface from uses statements.
*/
+++ /dev/null
-
-module mod-box {
- yang-version 1;
- namespace "urn:opendaylight:mod:box";
- prefix "box";
-
- revision 2014-08-05 {
- }
-
- container cont1 {
- choice choice1 {
- case case1 {
- container cont2 {
- leaf leaf1 {
- type string;
- }
- }
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.sal.binding.model.api;
-
-/**
- * Implementing this interface allows an object to hold information about that if
- * is generated type suitable for boxing.
- *
- * Example:
- * choice foo-choice {
- * case foo-case {
- * container foo {
- * ...
- * }
- * }
- * }
- *
- * Suitable type have to implements ChildOf<T>, where !(T instanceof Identifiable) and
- * T does not place any structural requirements (must/when) on existence/value Foo.
- */
-public interface BoxableType {
-
- /**
- * Check if generated type is suitable for boxing.
- *
- * @return true if generated type is suitable for boxing, false otherwise.
- */
- boolean isSuitableForBoxing();
-}
* definitions MUST be public, so there is no need to specify the scope of
* visibility.
*/
-public interface GeneratedType extends Type, DocumentedType, BoxableType {
+public interface GeneratedType extends Type, DocumentedType {
/**
* Returns the parent type if Generated Type is defined as enclosing type,
/**
* Generated Type Builder interface is helper interface for building and
* defining the GeneratedType.
- *
+ *
* @see GeneratedType
*/
public interface GeneratedTypeBuilder extends GeneratedTypeBuilderBase<GeneratedTypeBuilder> {
/**
* Returns the <code>new</code> <i>immutable</i> instance of Generated Type.
- *
+ *
* @return the <code>new</code> <i>immutable</i> instance of Generated Type.
*/
GeneratedType toInstance();
- /**
- * Set true if generated type is suitable for boxing, false otherwise.
- *
- * @param value
- */
- public void setSuitableForBoxing(boolean value);
-
- /**
- * Set parent for current generated type.
- *
- * @param parent
- */
- public void setParentType(GeneratedTypeBuilder parent);
}
package org.opendaylight.yangtools.sal.binding.model.api.type.builder;
import java.util.List;
+
import org.opendaylight.yangtools.sal.binding.model.api.Constant;
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.yang.common.QName;
import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Set;
import javax.xml.stream.XMLStreamReader;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This is an experimental iterator over a {@link NormalizedNode}. This is essentially
* us to write multiple nodes.
*/
@Beta
-public final class NormalizedNodeWriter implements Closeable, Flushable {
+public class NormalizedNodeWriter implements Closeable, Flushable {
private final NormalizedNodeStreamWriter writer;
private NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
this.writer = Preconditions.checkNotNull(writer);
}
+ protected final NormalizedNodeStreamWriter getWriter() {
+ return writer;
+ }
+
/**
* Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
*
- * @param writer Backend writer
+ * @param writer Back-end writer
* @return A new instance.
*/
public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
- return new NormalizedNodeWriter(writer);
+ return forStreamWriter(writer, true);
+ }
+
+ /**
+ * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple {@link #forStreamWriter(NormalizedNodeStreamWriter)}
+ * method, this allows the caller to switch off RFC6020 XML compliance, providing better
+ * throughput. The reason is that the XML mapping rules in RFC6020 require the encoding
+ * to emit leaf nodes which participate in a list's key first and in the order in which
+ * they are defined in the key. For JSON, this requirement is completely relaxed and leaves
+ * can be ordered in any way we see fit. The former requires a bit of work: first a lookup
+ * for each key and then for each emitted node we need to check whether it was already
+ * emitted.
+ *
+ * @param writer Back-end writer
+ * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
+ * @return A new instance.
+ */
+ public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, final boolean orderKeyLeaves) {
+ if (orderKeyLeaves) {
+ return new OrderedNormalizedNodeWriter(writer);
+ } else {
+ return new NormalizedNodeWriter(writer);
+ }
}
/**
* @return
* @throws IOException when thrown from the backing writer.
*/
- public NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+ public final NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
if (wasProcessedAsCompositeNode(node)) {
return this;
}
throw new IllegalStateException("It wasn't possible to serialize node " + node);
}
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+ /**
+ * Emit a best guess of a hint for a particular set of children. It evaluates the
+ * iterable to see if the size can be easily gotten to. If it is, we hint at the
+ * real number of child nodes. Otherwise we emit UNKNOWN_SIZE.
+ *
+ * @param children Child nodes
+ * @return Best estimate of the collection size required to hold all the children.
+ */
+ static final int childSizeHint(final Iterable<?> children) {
+ return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
+ }
+
private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
if (node instanceof LeafSetEntryNode) {
final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
return false;
}
+ /**
+ * Emit events for all children and then emit an endNode() event.
+ *
+ * @param children Child iterable
+ * @return True
+ * @throws IOException when the writer reports it
+ */
+ protected final boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
+ for (NormalizedNode<?, ?> child : children) {
+ write(child);
+ }
+
+ writer.endNode();
+ return true;
+ }
+
+ protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+ writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+ return writeChildren(node.getValue());
+ }
+
private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
- boolean hasDataContainerChild = false;
if (node instanceof ContainerNode) {
- writer.startContainerNode(((ContainerNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- } else if (node instanceof MapEntryNode) {
- writer.startMapEntryNode(((MapEntryNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- } else if (node instanceof UnkeyedListEntryNode) {
- writer.startUnkeyedListItem(((UnkeyedListEntryNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- } else if (node instanceof ChoiceNode) {
- writer.startChoiceNode(((ChoiceNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- } else if (node instanceof AugmentationNode) {
- writer.startAugmentationNode(((AugmentationNode) node).getIdentifier());
- hasDataContainerChild = true;
- } else if (node instanceof UnkeyedListNode) {
- writer.startUnkeyedList(((UnkeyedListNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- } else if (node instanceof OrderedMapNode) {
- writer.startOrderedMapNode(((OrderedMapNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- } else if (node instanceof MapNode) {
- writer.startMapNode(((MapNode) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- //covers also OrderedLeafSetNode for which doesn't exist start* method
- } else if (node instanceof LeafSetNode) {
- writer.startLeafSet(((LeafSetNode<?>) node).getIdentifier(), UNKNOWN_SIZE);
- hasDataContainerChild = true;
- }
-
- if (hasDataContainerChild) {
- for (NormalizedNode<?, ?> childNode : ((NormalizedNode<?, Iterable<NormalizedNode<?, ?>>>) node).getValue()) {
- write(childNode);
- }
-
- writer.endNode();
- return true;
+ final ContainerNode n = (ContainerNode) node;
+ writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof MapEntryNode) {
+ return writeMapEntryNode((MapEntryNode) node);
+ }
+ if (node instanceof UnkeyedListEntryNode) {
+ final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
+ writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof ChoiceNode) {
+ final ChoiceNode n = (ChoiceNode) node;
+ writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof AugmentationNode) {
+ final AugmentationNode n = (AugmentationNode) node;
+ writer.startAugmentationNode(n.getIdentifier());
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof UnkeyedListNode) {
+ final UnkeyedListNode n = (UnkeyedListNode) node;
+ writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof OrderedMapNode) {
+ final OrderedMapNode n = (OrderedMapNode) node;
+ writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof MapNode) {
+ final MapNode n = (MapNode) node;
+ writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
+ }
+ if (node instanceof LeafSetNode) {
+ //covers also OrderedLeafSetNode for which doesn't exist start* method
+ final LeafSetNode<?> n = (LeafSetNode<?>) node;
+ writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+ return writeChildren(n.getValue());
}
- return false;
+ return false;
}
- @Override
- public void flush() throws IOException {
- writer.flush();
- }
+ private static final class OrderedNormalizedNodeWriter extends NormalizedNodeWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(OrderedNormalizedNodeWriter.class);
- @Override
- public void close() throws IOException {
- writer.close();
+ OrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+ super(writer);
+ }
+
+ @Override
+ protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+ getWriter().startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+
+ final Set<QName> qnames = node.getIdentifier().getKeyValues().keySet();
+ // Write out all the key children
+ for (QName qname : qnames) {
+ final Optional<? extends NormalizedNode<?, ?>> child = node.getChild(new NodeIdentifier(qname));
+ if (child.isPresent()) {
+ write(child.get());
+ } else {
+ LOG.info("No child for key element {} found", qname);
+ }
+ }
+
+ // Write all the rest
+ return writeChildren(Iterables.filter(node.getValue(), new Predicate<NormalizedNode<?, ?>>() {
+ @Override
+ public boolean apply(final NormalizedNode<?, ?> input) {
+ if (input instanceof AugmentationNode) {
+ return true;
+ }
+ if (!qnames.contains(input.getNodeType())) {
+ return true;
+ }
+
+ LOG.debug("Skipping key child {}", input);
+ return false;
+ }
+ }));
+ }
}
}
*/
package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
import java.util.HashMap;
import java.util.Map;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
import org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
/**
* A TreeNode capable of holding child nodes. The fact that any of the children
* changed is tracked by the subtree version.
*/
-final class ContainerNode extends AbstractTreeNode {
- private final Map<PathArgument, TreeNode> children;
+abstract class ContainerNode extends AbstractTreeNode {
private final Version subtreeVersion;
- protected ContainerNode(final NormalizedNode<?, ?> data, final Version version,
- final Map<PathArgument, TreeNode> children, final Version subtreeVersion) {
+ protected ContainerNode(final NormalizedNode<?, ?> data, final Version version, final Version subtreeVersion) {
super(data, version);
- this.children = Preconditions.checkNotNull(children);
this.subtreeVersion = Preconditions.checkNotNull(subtreeVersion);
}
@Override
- public Version getSubtreeVersion() {
+ public final Version getSubtreeVersion() {
return subtreeVersion;
}
- @Override
- public Optional<TreeNode> getChild(final PathArgument key) {
- Optional<TreeNode> explicitNode = Optional.fromNullable(children.get(key));
- if (explicitNode.isPresent()) {
- return explicitNode;
- }
- final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> castedData = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) getData();
- Optional<NormalizedNode<?, ?>> value = castedData.getChild(key);
- if (value.isPresent()) {
- //FIXME: consider caching created Tree Nodes.
- //We are safe to not to cache them, since written Tree Nodes are in read only snapshot.
- return Optional.of(TreeNodeFactory.createTreeNode(value.get(), getVersion()));
- }
- return Optional.absent();
- }
-
- @Override
- public MutableTreeNode mutable() {
- return new Mutable(this);
- }
-
- private static final class Mutable implements MutableTreeNode {
+ protected static final class Mutable implements MutableTreeNode {
private final Version version;
private Map<PathArgument, TreeNode> children;
private NormalizedNode<?, ?> data;
private Version subtreeVersion;
- private Mutable(final ContainerNode parent) {
+ Mutable(final ContainerNode parent, final Map<PathArgument, TreeNode> children) {
this.data = parent.getData();
- this.children = MapAdaptor.getDefaultInstance().takeSnapshot(parent.children);
- this.subtreeVersion = parent.getSubtreeVersion();
this.version = parent.getVersion();
- materializeChildVersion();
- }
-
- /**
- * Traverse whole data tree and instantiate children for each data node. Set version of each MutableTreeNode
- * accordingly to version in data node.
- *
- * Use this method if TreeNode is lazy initialized.
- */
- private void materializeChildVersion() {
- Preconditions.checkState(data instanceof NormalizedNodeContainer);
- NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> castedData = (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) data;
-
- for(NormalizedNode<?, ?> childData : castedData.getValue()) {
- PathArgument id = childData.getIdentifier();
-
- if (!children.containsKey(id)) {
- children.put(id, TreeNodeFactory.createTreeNode(childData, version));
- }
- }
+ this.subtreeVersion = parent.getSubtreeVersion();
+ this.children = Preconditions.checkNotNull(children);
}
@Override
@Override
public TreeNode seal() {
- final TreeNode ret = new ContainerNode(data, version, MapAdaptor.getDefaultInstance().optimize(children), subtreeVersion);
+ final TreeNode ret = new MaterializedContainerNode(data, version, MapAdaptor.getDefaultInstance().optimize(children), subtreeVersion);
// This forces a NPE if this class is accessed again. Better than corruption.
children = null;
map.put(child.getIdentifier(), TreeNodeFactory.createTreeNodeRecursively(child, version));
}
- return new ContainerNode(data, version, map, version);
+ return new MaterializedContainerNode(data, version, map, version);
}
/**
*/
public static ContainerNode createNormalizedNode(final Version version,
final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> container) {
- return createNode(version, container);
+ return new LazyContainerNode(container, version);
}
/**
*/
public static ContainerNode createOrderedNode(final Version version,
final OrderedNodeContainer<NormalizedNode<?, ?>> container) {
- return createNode(version, container);
- }
-
- /**
- * Creates and returns single instance of {@link ContainerNode} with provided version and data reference stored in NormalizedNode.
- *
- * @param version version of indexed data
- * @param data NormalizedNode data container
- * @return single instance of {@link ContainerNode} with provided version and data reference stored in NormalizedNode.
- */
- private static ContainerNode createNode(final Version version, final NormalizedNode<?, ?> data) {
- final Map<PathArgument, TreeNode> map = new HashMap<>();
- return new ContainerNode(data, version, map, version);
+ return new LazyContainerNode(container, version);
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.data.api.schema.tree.spi;
+
+import com.google.common.base.Optional;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+final class LazyContainerNode extends ContainerNode {
+ protected LazyContainerNode(final NormalizedNode<?, ?> data, final Version version) {
+ super(data, version, version);
+ }
+
+ @Override
+ public Optional<TreeNode> getChild(final PathArgument key) {
+ // We do not cache the instantiated node as it is dirt cheap
+ final Optional<NormalizedNode<?, ?>> child = castData().getChild(key);
+ if (child.isPresent()) {
+ return Optional.of(TreeNodeFactory.createTreeNode(child.get(), getVersion()));
+ }
+
+ return Optional.absent();
+ }
+
+ @Override
+ public MutableTreeNode mutable() {
+ /*
+ * We are creating a mutable view of the data, which means that the version
+ * is going to probably change -- and we need to make sure any unmodified
+ * children retain it.
+ *
+ * The simplest thing to do is to just flush the amortized work and be done
+ * with it.
+ */
+ final Map<PathArgument, TreeNode> children = new HashMap<>();
+ for (NormalizedNode<?, ?> child : castData().getValue()) {
+ PathArgument id = child.getIdentifier();
+ children.put(id, TreeNodeFactory.createTreeNode(child, getVersion()));
+ }
+
+ return new Mutable(this, children);
+ }
+
+ @SuppressWarnings("unchecked")
+ private final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> castData() {
+ return (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) getData();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.data.api.schema.tree.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class MaterializedContainerNode extends ContainerNode {
+ private final Map<PathArgument, TreeNode> children;
+
+ protected MaterializedContainerNode(final NormalizedNode<?, ?> data, final Version version,
+ final Map<PathArgument, TreeNode> children, final Version subtreeVersion) {
+ super(data, version, subtreeVersion);
+ this.children = Preconditions.checkNotNull(children);
+ }
+
+ @Override
+ public Optional<TreeNode> getChild(final PathArgument key) {
+ return Optional.fromNullable(children.get(key));
+ }
+
+ @Override
+ public MutableTreeNode mutable() {
+ return new Mutable(this, MapAdaptor.getDefaultInstance().takeSnapshot(children));
+ }
+}
<groupId>${project.groupId}</groupId>
<artifactId>yang-data-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-data-util</artifactId>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>yang-model-api</artifactId>
--- /dev/null
+/*
+ * Copyright (c) 2014 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.data.impl.codec.xml;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringIdentityrefCodec;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Element;
+
+final class ElementIdentityrefParser extends AbstractStringIdentityrefCodec {
+ private final SchemaContext schema;
+ private final Element element;
+
+ ElementIdentityrefParser(final SchemaContext schema, final Element element) {
+ this.element = Preconditions.checkNotNull(element);
+ this.schema = Preconditions.checkNotNull(schema);
+ }
+
+ @Override
+ protected String prefixForNamespace(final URI namespace) {
+ return element.lookupPrefix(namespace.toString());
+ }
+
+ @Override
+ protected QName createQName(final String prefix, final String localName) {
+ final String namespace = element.lookupNamespaceURI(prefix);
+ Preconditions.checkArgument(namespace != null, "Failed to lookup prefix %s", prefix);
+
+ final URI ns = URI.create(namespace);
+ final Module module = schema.findModuleByNamespaceAndRevision(ns, null);
+ Preconditions.checkArgument(module != null, "Namespace %s is not owned by a module", ns);
+ return QName.create(module.getQNameModule(), localName);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.data.impl.codec.xml;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Element;
+
+final class ElementInstanceIdentifierParser extends AbstractStringInstanceIdentifierCodec {
+ private final SchemaContext schema;
+ private final Element element;
+
+ ElementInstanceIdentifierParser(final SchemaContext schema, final Element element) {
+ this.element = Preconditions.checkNotNull(element);
+ this.schema = Preconditions.checkNotNull(schema);
+ }
+
+ @Override
+ protected String prefixForNamespace(final URI namespace) {
+ return element.lookupPrefix(namespace.toString());
+ }
+
+ @Override
+ protected QName createQName(final String prefix, final String localName) {
+ final String namespace = element.lookupNamespaceURI(prefix);
+ Preconditions.checkArgument(namespace != null, "Failed to lookup prefix %s", prefix);
+
+ final URI ns = URI.create(namespace);
+ final Module module = schema.findModuleByNamespaceAndRevision(ns, null);
+ Preconditions.checkArgument(module != null, "Namespace %s is not owned by a module", ns);
+ return QName.create(module.getQNameModule(), localName);
+ }
+
+}
*/
package org.opendaylight.yangtools.yang.data.impl.codec.xml;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-
import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.w3c.dom.Element;
public final class InstanceIdentifierForXmlCodec {
- private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
- private static final Splitter SLASH_SPLITTER = Splitter.on('/');
- private static final Splitter COLON_SPLITTER = Splitter.on(':');
-
private InstanceIdentifierForXmlCodec() {
throw new UnsupportedOperationException("Utility class");
}
public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
- Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
- Preconditions.checkNotNull(schemaContext,
- "Schema context for deserialization of instance identifier type can't be null");
-
- final String valueTrimmed = element.getTextContent().trim();
- final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
-
- // must be at least "/pr:node"
- if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
- return null;
- }
-
- List<PathArgument> result = new ArrayList<>();
- while (xPathParts.hasNext()) {
- String xPathPartTrimmed = xPathParts.next().trim();
-
- PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
- if (pathArgument != null) {
- result.add(pathArgument);
- }
- }
- return YangInstanceIdentifier.create(result);
+ final ElementInstanceIdentifierParser codec = new ElementInstanceIdentifierParser(schemaContext, element);
+ return codec.deserialize(element.getTextContent().trim());
}
public static Element serialize(final YangInstanceIdentifier id, final Element element) {
- Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
- Preconditions.checkNotNull(element, "DOM element can't be null");
+ final RandomPrefixInstanceIdentifierSerializer codec = new RandomPrefixInstanceIdentifierSerializer();
+ final String str = codec.serialize(id);
- final RandomPrefix prefixes = new RandomPrefix();
- final String str = XmlUtils.encodeIdentifier(prefixes, id);
-
- for (Entry<URI, String> e: prefixes.getPrefixes()) {
+ for (Entry<URI, String> e : codec.getPrefixes()) {
element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
}
element.setTextContent(str);
return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
}
- private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
- final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
-
- // predicates
- final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
- final Map<QName, Object> predicates = new HashMap<>();
- QName currentQName = mainQName;
-
- while (matcher.find()) {
- final String predicateStr = matcher.group(1).trim();
- final int indexOfEqualityMark = predicateStr.indexOf('=');
- if (indexOfEqualityMark != -1) {
- final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
- if (predicateValue == null) {
- return null;
- }
-
- if (predicateStr.charAt(0) != '.') {
- // target is not a leaf-list
- currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
- if (currentQName == null) {
- return null;
- }
- }
- predicates.put(currentQName, predicateValue);
- }
- }
-
- if (predicates.isEmpty()) {
- return new YangInstanceIdentifier.NodeIdentifier(mainQName);
- } else {
- return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
- }
-
- }
-
public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
- final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
- final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
-
- // Empty string
- if (!it.hasNext()) {
- return null;
- }
-
- final String prefix = it.next().trim();
- if (prefix.isEmpty()) {
- return null;
- }
-
- // it is not "prefix:value"
- if (!it.hasNext()) {
- return null;
- }
-
- final String identifier = it.next().trim();
- if (identifier.isEmpty()) {
- return null;
- }
-
- URI namespace = null;
- String namespaceStr = null;
- try {
- namespaceStr = element.lookupNamespaceURI(prefix);
- namespace = new URI(namespaceStr);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
- } catch (NullPointerException e) {
- throw new IllegalArgumentException("It wasn't possible to get namespace for prefix " + prefix);
- }
-
- final Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
- Preconditions.checkNotNull(module, "Unknown module: %s, cannot parse identity %s", namespace, xPathArgument);
- return QName.create(module.getQNameModule(), identifier);
- }
-
- private static String trimIfEndIs(final String str, final char end) {
- final int l = str.length() - 1;
- if (str.charAt(l) != end) {
- return null;
- }
-
- return str.substring(1, l);
+ final ElementIdentityrefParser codec = new ElementIdentityrefParser(schemaContext, element);
+ return codec.deserialize(getIdAndPrefixAsStr(xPathArgument).trim());
}
- private static String toPredicateValue(final String predicatedValue) {
- final String predicatedValueTrimmed = predicatedValue.trim();
- if (predicatedValue.isEmpty()) {
- return null;
- }
-
- switch (predicatedValueTrimmed.charAt(0)) {
- case '"':
- return trimIfEndIs(predicatedValueTrimmed, '"');
- case '\'':
- return trimIfEndIs(predicatedValueTrimmed, '\'');
- default:
- return null;
- }
- }
}
package org.opendaylight.yangtools.yang.data.impl.codec.xml;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
+import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+
import java.net.URI;
import java.util.Map;
-import org.opendaylight.yangtools.yang.common.QName;
-
-final class RandomPrefix {
- public static final char STARTING_CHAR = 'a';
- public static final int CHARACTER_RANGE = 26;
- public static final int PREFIX_MAX_LENGTH = 4;
+import javax.xml.namespace.NamespaceContext;
- public static final int MAX_COUNTER_VALUE = (int) Math.pow(CHARACTER_RANGE, PREFIX_MAX_LENGTH);
- private static final int STARTING_WITH_XML = decode("xml");
+class RandomPrefix {
+ // 32 characters, carefully chosen
+ private static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
+ private static final int MASK = 0x1f;
+ private static final int SHIFT = 5;
private int counter = 0;
// BiMap to make values lookup faster
private final BiMap<URI, String> prefixes = HashBiMap.create();
+ private final NamespaceContext context;
- Iterable<Map.Entry<URI, String>> getPrefixes() {
- return prefixes.entrySet();
+ RandomPrefix() {
+ this.context = null;
}
- String encodeQName(final QName qname) {
- return encodePrefix(qname) + ':' + qname.getLocalName();
+ RandomPrefix(final NamespaceContext context) {
+ this.context = Preconditions.checkNotNull(context);
+ }
+
+ Iterable<Map.Entry<URI, String>> getPrefixes() {
+ return prefixes.entrySet();
}
- String encodePrefix(final QName qname) {
- String prefix = prefixes.get(qname.getNamespace());
+ String encodePrefix(final URI namespace) {
+ String prefix = prefixes.get(namespace);
if (prefix != null) {
return prefix;
}
- // Reuse prefix from QName if possible
- final String qNamePrefix = qname.getPrefix();
-
- if (!Strings.isNullOrEmpty(qNamePrefix) && !qNamePrefix.startsWith("xml") && !alreadyUsedPrefix(qNamePrefix)) {
- prefix = qNamePrefix;
- } else {
-
- do {
- // Skip values starting with xml (Expecting only 4 chars max since division is calculated only once)
- while (counter == STARTING_WITH_XML
- || counter / CHARACTER_RANGE == STARTING_WITH_XML) {
- counter++;
- }
-
- // Reset in case of max prefix generated
- if (counter >= MAX_COUNTER_VALUE) {
- counter = 0;
- prefixes.clear();
- }
-
- prefix = encode(counter);
- counter++;
- } while (alreadyUsedPrefix(prefix));
- }
+ do {
+ prefix = encode(counter);
+ counter++;
+ } while (alreadyUsedPrefix(prefix));
- prefixes.put(qname.getNamespace(), prefix);
+ prefixes.put(namespace, prefix);
return prefix;
}
private boolean alreadyUsedPrefix(final String prefix) {
- return prefixes.values().contains(prefix);
+ return context != null && context.getNamespaceURI(prefix) != null;
}
@VisibleForTesting
- static int decode(final String s) {
- int num = 0;
- for (final char ch : s.toCharArray()) {
- num *= CHARACTER_RANGE;
- num += (ch - STARTING_CHAR);
+ static int decode(final String str) {
+ int ret = 0;
+ for (char c : str.toCharArray()) {
+ int idx = LOOKUP.indexOf(c);
+ Preconditions.checkArgument(idx != -1, "Invalid string %s", str);
+ ret = (ret << SHIFT) + idx;
}
- return num;
+
+ return ret;
}
@VisibleForTesting
static String encode(int num) {
- if (num == 0) {
- return "a";
- }
-
final StringBuilder sb = new StringBuilder();
- while (num != 0) {
- sb.append(((char) (num % CHARACTER_RANGE + STARTING_CHAR)));
- num /= CHARACTER_RANGE;
- }
+
+ do {
+ sb.append(LOOKUP.charAt(num & MASK));
+ num >>>= SHIFT;
+ } while (num != 0);
return sb.reverse().toString();
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.data.impl.codec.xml;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
+
+final class RandomPrefixInstanceIdentifierSerializer extends AbstractStringInstanceIdentifierCodec {
+ private final RandomPrefix prefixes = new RandomPrefix();
+
+ Iterable<Map.Entry<URI, String>> getPrefixes() {
+ return prefixes.getPrefixes();
+ }
+
+ @Override
+ protected String prefixForNamespace(final URI namespace) {
+ return prefixes.encodePrefix(namespace);
+ }
+
+ @Override
+ protected QName createQName(final String prefix, final String localName) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+}
static void writeAttribute(final XMLStreamWriter writer, final Entry<QName, String> attribute, final RandomPrefix randomPrefix)
throws XMLStreamException {
final QName key = attribute.getKey();
- final String prefix = randomPrefix.encodePrefix(key);
+ final String prefix = randomPrefix.encodePrefix(key.getNamespace());
writer.writeAttribute("xmlns:" + prefix, key.getNamespace().toString());
writer.writeAttribute(prefix, key.getNamespace().toString(), key.getLocalName(), attribute.getValue());
}
package org.opendaylight.yangtools.yang.data.impl.codec.xml;
import java.util.Map;
+
import javax.annotation.Nonnull;
+
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
StringBuilder textContent = new StringBuilder();
for (PathArgument pathArgument : id.getPathArguments()) {
textContent.append('/');
- textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
+
+ final QName nt = pathArgument.getNodeType();
+ textContent.append(prefixes.encodePrefix(nt.getNamespace()));
+ textContent.append(':');
+ textContent.append(nt.getLocalName());
+
if (pathArgument instanceof NodeIdentifierWithPredicates) {
Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
for (Map.Entry<QName, Object> entry : predicates.entrySet()) {
- String predicateValue = String.valueOf(entry.getValue());
+ final QName key = entry.getKey();
textContent.append('[');
- textContent.append(prefixes.encodeQName(entry.getKey()));
+ textContent.append(prefixes.encodePrefix(key.getNamespace()));
+ textContent.append(':');
+ textContent.append(key.getLocalName());
textContent.append("='");
- textContent.append(predicateValue);
+ textContent.append(String.valueOf(entry.getValue()));
textContent.append("']");
}
} else if (pathArgument instanceof NodeWithValue) {
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+
import java.net.URI;
import java.util.Date;
import java.util.List;
+
import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
public class RandomPrefixTest {
+ static final int MAX_COUNTER = 4000;
@Test
public void testEncodeDecode() throws Exception {
+
final List<String> allGenerated = Lists.newArrayList();
- for (int i = 0; i < RandomPrefix.MAX_COUNTER_VALUE; i++) {
+ for (int i = 0; i < MAX_COUNTER; i++) {
final String encoded = RandomPrefix.encode(i);
assertEquals(RandomPrefix.decode(encoded), i);
allGenerated.add(encoded);
}
- assertEquals(allGenerated.size(), RandomPrefix.MAX_COUNTER_VALUE);
- assertEquals("zzzz", allGenerated.get(RandomPrefix.MAX_COUNTER_VALUE - 1));
+ assertEquals(allGenerated.size(), MAX_COUNTER);
+ assertEquals("dPT", allGenerated.get(MAX_COUNTER - 1));
assertEquals("a", allGenerated.get(0));
- assertEquals("xml", allGenerated.get(RandomPrefix.decode("xml")));
assertEquals(allGenerated.size(), Sets.newHashSet(allGenerated).size());
}
final RandomPrefix a = new RandomPrefix();
final List<String> allGenerated = Lists.newArrayList();
- for (int i = 0; i < RandomPrefix.MAX_COUNTER_VALUE; i++) {
+ for (int i = 0; i < MAX_COUNTER; i++) {
final String prefix = RandomPrefix.encode(i);
final URI uri = new URI("localhost:" + prefix);
- final QName qName = QName.create(QNameModule.create(uri, new Date()), prefix, "local-name");
- allGenerated.add(a.encodePrefix(qName));
+ final QName qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+ allGenerated.add(a.encodePrefix(qName.getNamespace()));
}
- assertEquals(RandomPrefix.MAX_COUNTER_VALUE, allGenerated.size());
+ assertEquals(MAX_COUNTER, allGenerated.size());
// We are generating MAX_COUNTER_VALUE + 27 prefixes total, so we should encounter a reset in prefix a start from 0 at some point
// At the end, there should be only 27 values in RandomPrefix cache
- assertEquals(27, Iterables.size(a.getPrefixes()));
+ assertEquals(MAX_COUNTER, Iterables.size(a.getPrefixes()));
assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xml")));
assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmla")));
assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmlz")));
- assertEquals(2, Iterables.frequency(allGenerated, "a"));
+ assertEquals(1, Iterables.frequency(allGenerated, "a"));
}
@Test
final QName qName = QName.create(QNameModule.create(uri, new Date()), "p1", "local-name");
final QName qName2 = QName.create(QNameModule.create(uri, new Date()), "p2", "local-name");
- assertEquals(a.encodePrefix(qName), a.encodePrefix(qName2));
+ assertEquals(a.encodePrefix(qName.getNamespace()), a.encodePrefix(qName2.getNamespace()));
}
@Test
final URI uri = URI.create("localhost");
QName qName = QName.create(uri, new Date(), "local-name");
- assertEquals("a", a.encodePrefix(qName));
+ assertEquals("a", a.encodePrefix(qName.getNamespace()));
qName = QName.create(QNameModule.create(uri, new Date()), "", "local-name");
- assertEquals("a", a.encodePrefix(qName));
+ assertEquals("a", a.encodePrefix(qName.getNamespace()));
qName = QName.create(QNameModule.create(URI.create("second"), new Date()), "", "local-name");
- assertEquals("b", a.encodePrefix(qName));
+ assertEquals("b", a.encodePrefix(qName.getNamespace()));
}
}
assertEquals(2, mappedPrefixes.size());
final String randomPrefixValue = mappedPrefixes.get("namespace2");
- final String expectedXmlAsString = "<element xmlns:prefix=\"namespace\" prefix:attr=\"value\" xmlns:" + randomPrefixValue + "=\"namespace2\" " + randomPrefixValue + ":attr=\"value\"></element>";
+ final String expectedXmlAsString = "<element xmlns:a=\"namespace\" a:attr=\"value\" xmlns:" + randomPrefixValue + "=\"namespace2\" " + randomPrefixValue + ":attr=\"value\"></element>";
XMLUnit.setIgnoreAttributeOrder(true);
final Document control = XMLUnit.buildControlDocument(expectedXmlAsString);
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import com.google.common.base.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+import static org.junit.Assert.assertEquals;
+
+public class BaseConstraintsTest {
+
+ @Test
+ public void canCreateConstraints() {
+ Number min = 5;
+ Number max = 99;
+ String description = "Any description";
+ String reference = "any_ref";
+ String reg_exp = "x|z";
+ Optional<String> desc = Optional.of(description);
+ Optional<String> ref = Optional.of(reference);
+
+ LengthConstraint lengthCons = BaseConstraints.newLengthConstraint(min, max, desc, ref);
+
+ assertEquals("LengthConstraints Get min", min, lengthCons.getMin());
+ assertEquals("LengthConstraints Get max", max, lengthCons.getMax());
+ assertEquals("LengthConstraints Get description", description, lengthCons.getDescription());
+ assertEquals("LengthConstraints Get reference", reference, lengthCons.getReference());
+
+ PatternConstraint patternCons = BaseConstraints.newPatternConstraint(reg_exp, desc, ref);
+
+ assertEquals("PatternConstraints Get regex", reg_exp, patternCons.getRegularExpression());
+ assertEquals("PatternConstraints Get description", description, patternCons.getDescription());
+ assertEquals("PatternConstraints Get reference", reference, patternCons.getReference());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import com.google.common.base.Optional;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+public class BaseTypesTest {
+
+ @Test
+ public void canCreateBaseTypes() {
+ Optional<?> int8 = BaseTypes.defaultBaseTypeFor("int8");
+ assertEquals(Optional.of(Int8.getInstance()), int8);
+
+ Optional<?> int16 = BaseTypes.defaultBaseTypeFor("int16");
+ assertEquals(Optional.of(Int16.getInstance()), int16);
+
+ Optional<?> int32 = BaseTypes.defaultBaseTypeFor("int32");
+ assertEquals(Optional.of(Int32.getInstance()), int32);
+
+ Optional<?> int64 = BaseTypes.defaultBaseTypeFor("int64");
+ assertEquals(Optional.of(Int64.getInstance()), int64);
+
+ Optional<?> int128 = BaseTypes.defaultBaseTypeFor("int128");
+ assertEquals("wrong type", Optional.absent(), int128);
+
+ Optional<?> uint8 = BaseTypes.defaultBaseTypeFor("uint8");
+ assertEquals(Optional.of(Uint8.getInstance()), uint8);
+
+ Optional<?> uint16 = BaseTypes.defaultBaseTypeFor("uint16");
+ assertEquals(Optional.of(Uint16.getInstance()), uint16);
+
+ Optional<?> uint32 = BaseTypes.defaultBaseTypeFor("uint32");
+ assertEquals(Optional.of(Uint32.getInstance()), uint32);
+
+ Optional<?> uint64 = BaseTypes.defaultBaseTypeFor("uint64");
+ assertEquals(Optional.of(Uint64.getInstance()), uint64);
+
+ Optional<?> uint128 = BaseTypes.defaultBaseTypeFor("uint128");
+ assertEquals("wrong type", Optional.absent(), uint128);
+
+ Optional<?> stringType = BaseTypes.defaultBaseTypeFor("string");
+ assertEquals(Optional.of(StringType.getInstance()), stringType);
+
+ Optional<?> binaryType = BaseTypes.defaultBaseTypeFor("binary");
+ assertEquals(Optional.of(BinaryType.getInstance()), binaryType);
+
+ Optional<?> booleanType = BaseTypes.defaultBaseTypeFor("boolean");
+ assertEquals(Optional.of(BooleanType.getInstance()), booleanType);
+
+ Optional<?> emptyType = BaseTypes.defaultBaseTypeFor("empty");
+ assertEquals(Optional.of(EmptyType.getInstance()), emptyType);
+
+ Optional<?> instance_identifier = BaseTypes.defaultBaseTypeFor("instance_identifier");
+ assertEquals(Optional.absent(), instance_identifier);
+
+ Optional<?> whatever = BaseTypes.defaultBaseTypeFor("whatever");
+ assertEquals("wrong type", Optional.absent(), whatever);
+
+ assertFalse("whatever is not build-in type", BaseTypes.isYangBuildInType("whatever"));
+ assertTrue("int8 is build-in type", BaseTypes.isYangBuildInType("int8"));
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+public class BinaryTypeTest {
+
+ @Test
+ public void canCreateBinaryType() {
+ BinaryType binType = BinaryType.getInstance();
+ BinaryType binType1 = BinaryType.getInstance();
+ String stringBinType = binType.toString();
+
+
+ List<LengthConstraint> lengthConstraints = binType.getLengthConstraints();
+ assertTrue(lengthConstraints.toString().contains("max=9223372036854775807"));
+ assertTrue(lengthConstraints.toString().contains("min=0"));
+
+ assertEquals("Default value is []", Collections.EMPTY_LIST, binType.getDefaultValue());
+ assertEquals("CURRENT", Status.CURRENT, binType.getStatus());
+ assertEquals("Base type is null", null, binType.getBaseType());
+ assertEquals("getQName gives BINARY_QNAME", BaseTypes.BINARY_QNAME, binType.getQName());
+ assertEquals("empty string", "", binType.getUnits());
+ assertEquals("getPath gives List of BINARY_QNAME",
+ Collections.singletonList(BaseTypes.BINARY_QNAME), binType.getPath().getPathFromRoot());
+
+ assertTrue("BinType.toString should contain Description", stringBinType.contains(binType.getDescription()));
+ assertTrue("BinType.toString should contain Reference", stringBinType.contains(binType.getReference()));
+
+ assertTrue("binType1 should equal to binType",
+ binType.equals(binType1) && binType1.equals(binType));
+ assertTrue("Hash code of binType and binType1 should be equal",
+ binType.hashCode() == binType1.hashCode());
+ assertEquals("binType should equals to itself", binType, binType);
+ assertFalse("binType shouldn't equal to null", binType.equals(null));
+ assertFalse("binType shouldn't equal to object of other type", binType.equals("str"));
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class BooleanTypeTest {
+
+ @Test
+ public void canCreateBooleanType() {
+ BooleanType boolType = BooleanType.getInstance();
+ String stringBoolType = boolType.toString();
+
+ assertEquals("getPath gives List of BOOLEAN_QNAME",
+ Collections.singletonList(BaseTypes.BOOLEAN_QNAME), boolType.getPath().getPathFromRoot());
+
+ assertEquals("getQName gives BOOLEAN_QNAME", BaseTypes.BOOLEAN_QNAME, boolType.getQName());
+
+ assertEquals("The boolean built-in type represents a boolean value.", boolType.getDescription());
+
+ String strPath = boolType.getPath().toString();
+ assertTrue("Should contain string of getPath", stringBoolType.contains(strPath));
+
+ assertEquals("Should be empty string", "", boolType.getUnits());
+
+ assertEquals("Base type is null", null, boolType.getBaseType());
+
+ assertEquals("Default value is false", false, boolType.getDefaultValue());
+
+ assertEquals("Status CURRENT", Status.CURRENT, boolType.getStatus());
+
+ assertEquals("Should contain empty list", Collections.EMPTY_LIST, boolType.getUnknownSchemaNodes());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+public class Decimal64Test {
+
+ @Test
+ public void canCreateDecimal64() {
+ Integer fractionDig = 2;
+ Decimal64 decimal64 = Decimal64.create(SchemaPath.ROOT, fractionDig);
+
+ assertEquals("Status should be CURRENT", Status.CURRENT, decimal64.getStatus());
+
+ assertEquals("Default value should be null", null, decimal64.getDefaultValue());
+
+ assertEquals("Should be empty list", Collections.EMPTY_LIST, decimal64.getUnknownSchemaNodes());
+
+ assertEquals("Should be null", null, decimal64.getBaseType());
+
+ assertNotEquals("Description is not null", null, decimal64.getDescription());
+
+ assertNotEquals("Reference is not null", null, decimal64.getReference());
+
+ assertEquals("Should be empty string", "", decimal64.getUnits());
+
+ assertTrue("Should contain factionDigits=2", decimal64.toString().contains("fractionDigits="+fractionDig));
+
+ assertEquals("Should get farctionDig", fractionDig, decimal64.getFractionDigits());
+
+ assertEquals("Should be empty list",
+ Collections.EMPTY_LIST, decimal64.getPath().getPathFromRoot());
+
+ assertEquals("Should be DECIMAL64_QNAME", BaseTypes.DECIMAL64_QNAME, decimal64.getQName());
+
+ assertTrue("Should contain max", decimal64.getRangeConstraints().toString().contains("max=922337203685477580.7"));
+ assertTrue("Should contain min", decimal64.getRangeConstraints().toString().contains("min=-922337203685477580.8"));
+
+ Decimal64 decimal641 = decimal64;
+ assertTrue("Hash code of decimal64 and decimal641 should be equal",
+ decimal64.hashCode() == decimal641.hashCode());
+
+ assertFalse("Decimal64 shouldn't equal to null", decimal64.equals(null));
+ assertEquals("Decimal64 should equals to itself", decimal64, decimal64);
+ assertFalse("Decimal64 shouldn't equal to object of other type", decimal64.equals("str"));
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+public class EmptyTypeTest {
+
+ @Test
+ public void canCreateEmptyType() {
+ EmptyType emptyType = EmptyType.getInstance();
+
+ assertEquals("QName", BaseTypes.EMPTY_QNAME, emptyType.getQName());
+ assertEquals("Path", Collections.singletonList(BaseTypes.EMPTY_QNAME),
+ emptyType.getPath().getPathFromRoot());
+ assertEquals("BaseType", null, emptyType.getBaseType());
+ assertEquals("DefaultValue", null, emptyType.getDefaultValue());
+ assertEquals("Status", Status.CURRENT, emptyType.getStatus());
+ assertTrue("Reference", emptyType.getReference().contains("rfc6020"));
+ assertEquals("Units", null, emptyType.getUnits());
+ assertNotEquals("Description is not null", null, emptyType.getDescription());
+ assertEquals("UnknownSchemaNodes", Collections.EMPTY_LIST, emptyType.getUnknownSchemaNodes());
+ assertTrue("toString", emptyType.toString().contains("empty"));
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import com.google.common.base.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+public class ExtendedTypeTest {
+ @Test
+ public void canCreateExtendedType() {
+ String namespace = "TestType";
+ String revision = "2014-08-26";
+ String localName = "testType";
+ QName testType = QName.create(namespace, revision, localName);
+ Int32 int32 = Int32.getInstance();
+ String description = "This type is used for testing purpose";
+ Optional<String> desc = Optional.of(description);
+ String reference = "Test Reference";
+ Optional<String> ref = Optional.of(reference);
+
+ ExtendedType.Builder extendedTypeBuilder = ExtendedType.builder(testType, int32, desc, ref, SchemaPath.ROOT);
+
+ int defValue = 12;
+ extendedTypeBuilder.defaultValue(defValue);
+
+ extendedTypeBuilder.status(Status.OBSOLETE);
+ extendedTypeBuilder.addedByUses(false);
+
+ int digits = 2;
+ extendedTypeBuilder.fractionDigits(digits);
+
+ String units = "KiloTest";
+ extendedTypeBuilder.units(units);
+
+ Number min = 3;
+ Number max = 15;
+ LengthConstraint lengthCons = BaseConstraints.newLengthConstraint(min, max, desc, ref);
+ extendedTypeBuilder.lengths(Collections.singletonList(lengthCons));
+
+ ExtendedType extendedType = extendedTypeBuilder.build();
+
+ assertEquals("BaseType is int32", int32, extendedType.getBaseType());
+ assertEquals("Description", description, extendedType.getDescription());
+ assertEquals("Reference", reference, extendedType.getReference());
+ assertEquals("Path", SchemaPath.ROOT, extendedType.getPath());
+ assertEquals("Default Value is 12", defValue, extendedType.getDefaultValue());
+ assertEquals("Status is OBSOLETE", Status.OBSOLETE, extendedType.getStatus());
+ assertFalse("AddedByUses", extendedType.isAddedByUses());
+ assertTrue("should be 2", digits == extendedType.getFractionDigits());
+ assertTrue("Should contain description", extendedType.toString().contains(description));
+ assertEquals("Units", units, extendedType.getUnits());
+ assertEquals("Length Constraints", Collections.singletonList(lengthCons), extendedType.getLengthConstraints());
+ assertTrue("Should contain name of type", extendedType.getQName().toString().contains(localName));
+
+ assertEquals("extendedType should equals to itself",extendedType, extendedType);
+ assertFalse("extendedType shouldn't equal to null", extendedType.equals(null));
+ assertTrue("Hash code of unionType should be equal to itself",
+ extendedType.hashCode() == extendedType.hashCode());
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 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.util;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+public class UnionTypeTest {
+
+ @Test
+ public void canCreateUnion() {
+ List<TypeDefinition<?>> listTypes = new ArrayList<>();
+ Int32 int32 = Int32.getInstance();
+ listTypes.add(int32);
+ UnionType unionType = UnionType.create(listTypes);
+
+ assertEquals("GetUnits should be null", null, unionType.getUnits());
+ assertTrue("String should contain int32", unionType.toString().contains("int32"));
+ assertEquals("Should be empty list", Collections.EMPTY_LIST, unionType.getUnknownSchemaNodes());
+ assertNotEquals("Description should not be null", null, unionType.getDescription());
+ assertNotEquals("Ref should not be null", null, unionType.getReference());
+ assertEquals("Should be CURRENT", Status.CURRENT, unionType.getStatus());
+ assertEquals("Should be int32 in list", Collections.singletonList(int32), unionType.getTypes());
+ assertEquals("Base type should be null", null, unionType.getBaseType());
+ assertEquals("Default value should be null", null, unionType.getDefaultValue());
+ assertEquals("Should be same as list of BaseTypes",
+ Collections.singletonList(BaseTypes.UNION_QNAME), unionType.getPath().getPathFromRoot());
+ assertEquals("Should be BaseTypes", BaseTypes.UNION_QNAME, unionType.getQName());
+
+ assertEquals("unionType should equals to itself", unionType, unionType);
+ assertFalse("unionType shouldn't equal to null", unionType.equals(null));
+ assertTrue("Hash code of unionType should be equal to itself",
+ unionType.hashCode() == unionType.hashCode());
+ }
+
+}
\ No newline at end of file
*/
package org.opendaylight.yangtools.yang.parser.repo;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
-
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
/**
* Inter-module dependency resolved. Given a set of schema source identifiers and their
* corresponding dependency information, the {@link #create(Map)} method creates a
final YangModelDependencyInfo dep = depInfo.get(id);
boolean okay = true;
- for (ModuleImport mi : dep.getDependencies()) {
+
+ Set<ModuleImport> dependencies = dep.getDependencies();
+
+ // in case of submodule, make its parent also a dependency
+ if(dep instanceof YangModelDependencyInfo.SubmoduleDependencyInfo) {
+ final String parent = ((YangModelDependencyInfo.SubmoduleDependencyInfo) dep).getParentModule();
+ dependencies = Sets.newHashSet(dependencies);
+ dependencies.add(new BelongsToDependency(parent));
+ }
+
+ for (ModuleImport mi : dependencies) {
if (!isKnown(resolved, mi)) {
LOG.debug("Source {} is missing import {}", id, mi);
okay = false;
Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
return unsatisfiedImports;
}
+
+ private static class BelongsToDependency implements ModuleImport {
+ private final String parent;
+
+ public BelongsToDependency(final String parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public String getModuleName() {
+ return parent;
+ }
+
+ @Override
+ public Date getRevision() {
+ return null;
+ }
+
+ @Override
+ public String getPrefix() {
+ return null;
+ }
+ }
}
package org.opendaylight.yangtools.yang.parser.repo;
+import static org.junit.Assert.assertEquals;
+
import com.google.common.base.Optional;
import java.util.HashMap;
import java.util.Map;
-import junit.framework.Assert;
import org.junit.Test;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
final DependencyResolver resolved = DependencyResolver.create(map);
- Assert.assertEquals(0, resolved.getUnresolvedSources().size());
- Assert.assertEquals(0, resolved.getUnsatisfiedImports().size());
+ assertEquals(0, resolved.getUnresolvedSources().size());
+ assertEquals(0, resolved.getUnsatisfiedImports().size());
+ }
+
+ @Test
+ public void testSubmoduleNoModule() throws Exception {
+ final Map<SourceIdentifier, YangModelDependencyInfo> map = new HashMap<>();
+
+ // Subfoo does not have parent in reactor
+ addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.fromInputStream(getClass().getResourceAsStream("/model/subfoo.yang")));
+ addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.fromInputStream(getClass().getResourceAsStream("/model/bar.yang")));
+ addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.fromInputStream(getClass().getResourceAsStream("/model/baz.yang")));
+
+ final DependencyResolver resolved = DependencyResolver.create(map);
+
+ assertEquals(1, resolved.getUnresolvedSources().size());
+ assertEquals(0, resolved.getUnsatisfiedImports().size());
}
private void addToMap(final Map<SourceIdentifier, YangModelDependencyInfo> map, final YangModelDependencyInfo yangModelDependencyInfo) {