import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+
+import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+
import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeWriterFactory;
import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerRegistry, BindingNormalizedNodeWriterFactory, BindingNormalizedNodeSerializer {
+ private static final Logger LOG = LoggerFactory.getLogger(BindingNormalizedNodeCodecRegistry.class);
private final DataObjectSerializerGenerator generator;
private final LoadingCache<Class<? extends DataObject>, DataObjectSerializer> serializers;
// We create Binding Stream Writer wchich translates from Binding to Normalized Nodes
Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx = codecContext.newWriter(path, domWriter);
- // We get serializer which reads binding data and uses Binding To NOrmalized Node writer to write result
- getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
+ // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
+ try {
+ getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
+ } catch (IOException e) {
+ LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
+ throw new IllegalStateException("Failed to create normalized node", e);
+ }
return new SimpleEntry<YangInstanceIdentifier,NormalizedNode<?,?>>(writeCtx.getKey(),result.getResult());
}
}
@Override
- public void serialize(final DataObject obj, final BindingStreamEventWriter stream) {
+ public void serialize(final DataObject obj, final BindingStreamEventWriter stream) throws IOException {
delegate.serialize(BindingNormalizedNodeCodecRegistry.this, obj, stream);
}
}
import com.google.common.base.Preconditions;
+import java.io.IOException;
import java.util.AbstractMap;
import java.util.ArrayDeque;
import java.util.Deque;
}
@Override
- public void endNode() {
+ public void endNode() throws IOException {
NodeCodecContext left = schema.pop();
// NormalizedNode writer does not have entry into case, but into choice
// so for leaving case, we do not emit endNode.
}
@Override
- public void leafNode(final String localName, final Object value) throws IllegalArgumentException {
+ public void leafNode(final String localName, final Object value) throws IOException, IllegalArgumentException {
Entry<NodeIdentifier, Object> dom = serializeLeaf(localName, value);
getDelegate().leafNode(dom.getKey(), dom.getValue());
};
@Override
- public void anyxmlNode(final String name, final Object value) throws IllegalArgumentException {
+ public void anyxmlNode(final String name, final Object value) throws IOException, IllegalArgumentException {
Entry<NodeIdentifier, Object> dom = serializeLeaf(name, value);
getDelegate().anyxmlNode(dom.getKey(), dom.getValue());
}
@Override
- public void leafSetEntryNode(final Object value) throws IllegalArgumentException {
+ public void leafSetEntryNode(final Object value) throws IOException, IllegalArgumentException {
LeafNodeCodecContext ctx = (LeafNodeCodecContext) current();
getDelegate().leafSetEntryNode(ctx.getValueCodec().serialize(value));
}
@Override
public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
getDelegate().startAugmentationNode(enter(augmentationType, AugmentationIdentifier.class));
}
@Override
public void startChoiceNode(final Class<? extends DataContainer> type, final int childSizeHint)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
getDelegate().startChoiceNode(enter(type, NodeIdentifier.class), childSizeHint);
}
@Override
public void startContainerNode(final Class<? extends DataObject> object, final int childSizeHint)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
getDelegate().startContainerNode(enter(object, NodeIdentifier.class), childSizeHint);
}
@Override
- public void startLeafSet(final String localName, final int childSizeHint) throws IllegalArgumentException {
+ public void startLeafSet(final String localName, final int childSizeHint) throws IOException, IllegalArgumentException {
getDelegate().startLeafSet(enter(localName, NodeIdentifier.class), childSizeHint);
};
@Override
- public void startMapEntryNode(final Identifier<?> key, final int childSizeHint) throws IllegalArgumentException {
+ public void startMapEntryNode(final Identifier<?> key, final int childSizeHint) throws IOException, IllegalArgumentException {
duplicateSchemaEnter();
NodeIdentifierWithPredicates identifier = ((ListNodeCodecContext) current()).serialize(key);
getDelegate().startMapEntryNode(identifier, childSizeHint);
@Override
public <T extends DataObject & Identifiable<?>> void startMapNode(final Class<T> mapEntryType,
- final int childSizeHint) throws IllegalArgumentException {
+ final int childSizeHint) throws IOException, IllegalArgumentException {
getDelegate().startMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
};
@Override
public <T extends DataObject & Identifiable<?>> void startOrderedMapNode(final Class<T> mapEntryType,
- final int childSizeHint) throws IllegalArgumentException {
+ final int childSizeHint) throws IOException, IllegalArgumentException {
getDelegate().startOrderedMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
};
@Override
public void startUnkeyedList(final Class<? extends DataObject> obj, final int childSizeHint)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
getDelegate().startUnkeyedList(enter(obj, NodeIdentifier.class), childSizeHint);
};
@Override
- public void startUnkeyedListItem(final int childSizeHint) throws IllegalStateException {
+ public void startUnkeyedListItem(final int childSizeHint) throws IllegalStateException, IOException {
getDelegate().startUnkeyedListItem(duplicateSchemaEnter(), childSizeHint);
}
+ @Override
+ public void flush() throws IOException {
+ getDelegate().flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ getDelegate().close();
+ }
}
import com.google.common.base.Preconditions;
+import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collections;
@Override
public void serialize(final DataObjectSerializerRegistry reg, final DataObject obj,
- final BindingStreamEventWriter stream) {
+ final BindingStreamEventWriter stream) throws IOException {
if (obj instanceof Augmentable<?>) {
final Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations;
if (reg instanceof AugmentationReader) {
@SuppressWarnings("rawtypes")
private void emitAugmentation(final Class type, final Augmentation<?> value, final BindingStreamEventWriter stream,
- final DataObjectSerializerRegistry registry) {
+ final DataObjectSerializerRegistry registry) throws IOException {
Preconditions.checkArgument(value instanceof DataObject);
@SuppressWarnings("unchecked")
DataObjectSerializer serializer = registry.getSerializer(type);
import com.google.common.base.Preconditions;
+import java.io.IOException;
+
import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
@SuppressWarnings("unchecked")
@Override
- public void serialize(final DataObjectSerializerRegistry reg, final DataObject obj, final BindingStreamEventWriter stream) {
+ public void serialize(final DataObjectSerializerRegistry reg, final DataObject obj, final BindingStreamEventWriter stream) throws IOException {
@SuppressWarnings("rawtypes")
Class cazeClass = obj.getImplementedInterface();
stream.startChoiceNode(choiceClass, BindingStreamEventWriter.UNKNOWN_SIZE);
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.opendaylight.yangtools.binding.generator.util.BindingTypes;
import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
public class BindingGeneratorImpl implements BindingGenerator {
private static final Logger LOG = LoggerFactory.getLogger(BindingGeneratorImpl.class);
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+ private static final Splitter BSDOT_SPLITTER = Splitter.on("\\.");
+ private static final char NEW_LINE = '\n';
+
+ /**
+ * Constant with the concrete name of identifier.
+ */
+ private static final String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
+
+ /**
+ * Constant with the concrete name of namespace.
+ */
+ private static final String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
private final Map<Module, ModuleContext> genCtx = new HashMap<>();
+ /**
+ * When set to true, generated classes will include javadoc comments which
+ * are useful for users.
+ */
+ private final boolean verboseClassComments;
+
/**
* Outer key represents the package name. Outer value represents map of all
* builders in the same package. Inner key represents the schema node name
private SchemaContext schemaContext;
/**
- * Constant with the concrete name of namespace.
+ * Create a new binding generator with verboe comments.
+ *
+ * @deprecated Use {@link #BindingGeneratorImpl(boolean)} instead.
*/
- private final static String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
+ @Deprecated
+ public BindingGeneratorImpl() {
+ this(true);
+ }
/**
- * Constant with the concrete name of identifier.
+ * Create a new binding generator.
+ *
+ * @param verboseClassComments generate verbose comments
*/
- private final static String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
-
- private final char NEW_LINE = '\n';
-
- private final char TAB = '\t';
+ public BindingGeneratorImpl(final boolean verboseClassComments) {
+ this.verboseClassComments = verboseClassComments;
+ }
/**
* Resolves generated types from <code>context</code> schema nodes of all
newType.setDescription(createDescription(identity, newType.getFullyQualifiedName()));
newType.setReference(identity.getReference());
newType.setModuleName(module.getName());
- SchemaPath path = identity.getPath();
newType.setSchemaPath(identity.getPath().getPathFromRoot());
final QName qname = identity.getQName();
final String nodeParam = node.getNodeParameter();
IdentitySchemaNode identity = null;
String basePackageName = null;
- final Iterable<String> splittedElement = Splitter.on(':').split(nodeParam);
+ final Iterable<String> splittedElement = COLON_SPLITTER.split(nodeParam);
final Iterator<String> iterator = splittedElement.iterator();
final int length = Iterables.size(splittedElement);
if (length == 1) {
private String createDescription(final Set<? extends SchemaNode> schemaNodes, final String moduleName, final String moduleSourcePath) {
final StringBuilder sb = new StringBuilder();
- final String yangSnipet = YangTemplate.generateYangSnipet(schemaNodes);
if (!isNullOrEmpty(schemaNodes)) {
final SchemaNode node = schemaNodes.iterator().next();
if (node instanceof RpcDefinition) {
sb.append("Interface for implementing the following YANG RPCs defined in module <b>" + moduleName + "</b>");
- }
- else if (node instanceof NotificationDefinition) {
+ } else if (node instanceof NotificationDefinition) {
sb.append("Interface for receiving the following YANG notifications defined in module <b>" + moduleName + "</b>");
}
}
sb.append(moduleSourcePath);
sb.append("</i>):");
sb.append(NEW_LINE);
- sb.append("<pre>");
- sb.append(NEW_LINE);
- sb.append(yangSnipet);
- sb.append("</pre>");
- sb.append(NEW_LINE);
+
+ if (verboseClassComments) {
+ sb.append("<pre>");
+ sb.append(NEW_LINE);
+ sb.append(YangTemplate.generateYangSnipet(schemaNodes));
+ sb.append("</pre>");
+ sb.append(NEW_LINE);
+ }
return sb.toString();
}
private String createDescription(final SchemaNode schemaNode, final String fullyQualifiedName) {
final StringBuilder sb = new StringBuilder();
- final Module module = findParentModule(schemaContext, schemaNode);
- final String yangSnipet = YangTemplate.generateYangSnipet(schemaNode);
final String formattedDescription = YangTemplate.formatToParagraph(schemaNode.getDescription(), 0);
- final StringBuilder linkToBuilderClass = new StringBuilder();
- final StringBuilder linkToKeyClass = new StringBuilder();
- final Splitter splitter = Splitter.on("\\.");
- final String[] namespace = Iterables.toArray(splitter.split(fullyQualifiedName), String.class);
- String className = namespace[namespace.length - 1];
-
- if (hasBuilderClass(schemaNode)) {
- linkToBuilderClass.append(className);
- linkToBuilderClass.append("Builder");
-
- if (schemaNode instanceof ListSchemaNode) {
- linkToKeyClass.append(className);
- linkToKeyClass.append("Key");
- }
- }
if (!isNullOrEmpty(formattedDescription)) {
sb.append(formattedDescription);
sb.append(NEW_LINE);
}
- sb.append("<p>");
- sb.append("This class represents the following YANG schema fragment defined in module <b>");
- sb.append(module.getName());
- sb.append("</b>");
- sb.append(NEW_LINE);
- sb.append("<br />(Source path: <i>");
- sb.append(module.getModuleSourcePath());
- sb.append("</i>):");
- sb.append(NEW_LINE);
- sb.append("<pre>");
- sb.append(NEW_LINE);
- sb.append(yangSnipet);
- sb.append("</pre>");
- sb.append(NEW_LINE);
- sb.append("The schema path to identify an instance is");
- sb.append(NEW_LINE);
- sb.append("<i>");
- sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
- sb.append("</i>");
- sb.append(NEW_LINE);
- if (hasBuilderClass(schemaNode)) {
+ if (verboseClassComments) {
+ final Module module = findParentModule(schemaContext, schemaNode);
+ final StringBuilder linkToBuilderClass = new StringBuilder();
+ final StringBuilder linkToKeyClass = new StringBuilder();
+ final String[] namespace = Iterables.toArray(BSDOT_SPLITTER.split(fullyQualifiedName), String.class);
+ String className = namespace[namespace.length - 1];
+
+ if (hasBuilderClass(schemaNode)) {
+ linkToBuilderClass.append(className);
+ linkToBuilderClass.append("Builder");
+
+ if (schemaNode instanceof ListSchemaNode) {
+ linkToKeyClass.append(className);
+ linkToKeyClass.append("Key");
+ }
+ }
+
+ sb.append("<p>");
+ sb.append("This class represents the following YANG schema fragment defined in module <b>");
+ sb.append(module.getName());
+ sb.append("</b>");
+ sb.append(NEW_LINE);
+ sb.append("<br />(Source path: <i>");
+ sb.append(module.getModuleSourcePath());
+ sb.append("</i>):");
sb.append(NEW_LINE);
- sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+ sb.append("<pre>");
sb.append(NEW_LINE);
- sb.append("@see ");
- sb.append(linkToBuilderClass);
- if (schemaNode instanceof ListSchemaNode) {
+ sb.append(YangTemplate.generateYangSnipet(schemaNode));
+ sb.append("</pre>");
+ sb.append(NEW_LINE);
+ sb.append("The schema path to identify an instance is");
+ sb.append(NEW_LINE);
+ sb.append("<i>");
+ sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
+ sb.append("</i>");
+ sb.append(NEW_LINE);
+
+ if (hasBuilderClass(schemaNode)) {
+ sb.append(NEW_LINE);
+ sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+ sb.append(NEW_LINE);
sb.append("@see ");
- sb.append(linkToKeyClass);
+ sb.append(linkToBuilderClass);
+ if (schemaNode instanceof ListSchemaNode) {
+ sb.append("@see ");
+ sb.append(linkToKeyClass);
+ }
+ sb.append(NEW_LINE);
}
- sb.append(NEW_LINE);
}
return sb.toString();
private String createDescription(final Module module) {
final StringBuilder sb = new StringBuilder();
- final String yangSnipet = YangTemplate.generateYangSnipet(module);
final String formattedDescription = YangTemplate.formatToParagraph(module.getDescription(), 0);
if (!isNullOrEmpty(formattedDescription)) {
sb.append(formattedDescription);
sb.append(NEW_LINE);
}
- sb.append("<p>");
- sb.append("This class represents the following YANG schema fragment defined in module <b>");
- sb.append(module.getName());
- sb.append("</b>");
- sb.append(NEW_LINE);
- sb.append("<br />Source path: <i>");
- sb.append(module.getModuleSourcePath());
- sb.append("</i>):");
- sb.append(NEW_LINE);
- sb.append("<pre>");
- sb.append(NEW_LINE);
- sb.append(yangSnipet);
- sb.append("</pre>");
+
+ if (verboseClassComments) {
+ sb.append("<p>");
+ sb.append("This class represents the following YANG schema fragment defined in module <b>");
+ sb.append(module.getName());
+ sb.append("</b>");
+ sb.append(NEW_LINE);
+ sb.append("<br />Source path: <i>");
+ sb.append(module.getModuleSourcePath());
+ sb.append("</i>):");
+ sb.append(NEW_LINE);
+ sb.append("<pre>");
+ sb.append(NEW_LINE);
+ sb.append(YangTemplate.generateYangSnipet(module));
+ sb.append("</pre>");
+ }
return sb.toString();
}
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
+
import java.net.URI;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
+
import javassist.ClassPool;
+
import javax.annotation.concurrent.GuardedBy;
+
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
import org.opendaylight.yangtools.binding.generator.util.Types;
@GuardedBy("this")
private void recreateBindingContext(final SchemaContext schemaContext) {
- BindingGeneratorImpl newBinding = new BindingGeneratorImpl();
+ BindingGeneratorImpl newBinding = new BindingGeneratorImpl(false);
newBinding.generateTypes(schemaContext);
for (Map.Entry<Module, ModuleContext> entry : newBinding.getModuleContexts().entrySet()) {
*/
package org.opendaylight.yangtools.sal.binding.generator.impl
-import java.text.SimpleDateFormat
import java.util.Collection
import java.util.Date
import java.util.List
import org.opendaylight.yangtools.yang.model.api.UsesNode
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil
+import com.google.common.base.CharMatcher
class YangTemplate {
+ // FIXME: this is not thread-safe and seems to be unused!
private static var Module module = null
+ private static val CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t")
+
def static String generateYangSnipet(SchemaNode schemaNode) {
if (schemaNode == null)
return ''
'''
}
- def static formatDate(Date moduleRevision) {
- val SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd")
- return dateFormat.format(moduleRevision)
- }
-
def static writeRevision(Date moduleRevision, String moduleDescription) {
val revisionIndent = 12
'''
- revision «formatDate(moduleRevision)» {
+ revision «SimpleDateFormatUtil.getRevisionFormat.format(moduleRevision)» {
description "«formatToParagraph(moduleDescription, revisionIndent)»";
}
'''
val lineIndent = computeNextLineIndent(nextLineIndent);
formattedText = formattedText.replace("*/", "*/");
- formattedText = formattedText.replace("\n", "");
- formattedText = formattedText.replace("\t", "");
+ formattedText = NEWLINE_OR_TAB.removeFrom(formattedText);
formattedText = formattedText.replaceAll(" +", " ");
val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
val ns = pathElement.namespace
val localName = pathElement.localName
- sb.append("\\")
- sb.append('(')
+ sb.append("\\(")
sb.append(ns)
sb.append(')')
sb.append(localName)
this.strategy = strategy;
this.schemaContext = schema;
- BindingGeneratorImpl generator = new BindingGeneratorImpl();
+ BindingGeneratorImpl generator = new BindingGeneratorImpl(false);
generator.generateTypes(schema);
Map<Module, ModuleContext> modules = generator.getModuleContexts();
import static org.opendaylight.yangtools.yang.binding.BindingMapping.*
import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider
import com.google.common.base.Preconditions
+import org.opendaylight.yangtools.yang.binding.BindingMapping
class YangModuleInfoTemplate {
return INSTANCE;
}
- «module.classBody»
+ «classBody(module, MODULE_INFO_CLASS_NAME)»
}
'''
return '''
}
- private def CharSequence classBody(Module m) '''
- private «MODULE_INFO_CLASS_NAME»() {
- «IF m.imports.size != 0»
+ private def CharSequence classBody(Module m, String className) '''
+ private «className»() {
+ «IF !m.imports.empty || !m.submodules.empty»
«Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
+ «ENDIF»
+ «IF !m.imports.empty»
«FOR imp : m.imports»
«val name = imp.moduleName»
«val rev = imp.revision»
set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(ctx.findModuleByName(name, rev))».«MODULE_INFO_CLASS_NAME».getInstance());
«ENDIF»
«ENDFOR»
- importedModules = «ImmutableSet.importedName».copyOf(set);
- «ELSE»
+ «ENDIF»
+ «IF !m.submodules.empty»
+ «FOR submodule : m.submodules»
+ set.add(«BindingMapping.getClassName(submodule.name)»Info.getInstance());
+ «ENDFOR»
+ «ENDIF»
+ «IF m.imports.empty && m.submodules.empty»
importedModules = «Collections.importedName».emptySet();
+ «ELSE»
+ importedModules = «ImmutableSet.importedName».copyOf(set);
«ENDIF»
+
«InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
if (stream == null) {
throw new IllegalStateException("Resource '" + resourcePath + "' is missing");
sb.append("]");
return sb.toString();
}
+
+ «generateSubInfo(m)»
+
'''
def getSourcePath() {
return builder.toString();
}
+ private def generateSubInfo(Module module) '''
+ «FOR submodule : module.submodules»
+ private static final class «BindingMapping.getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
+
+ private static final «YangModuleInfo.importedName» INSTANCE = new «BindingMapping.getClassName(submodule.name)»Info();
+
+ private final «String.importedName» name = "«submodule.name»";
+ private final «String.importedName» namespace = "«submodule.namespace.toString»";
+ «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
+ private final «String.importedName» revision = "«df.format(submodule.revision)»";
+ private final «String.importedName» resourcePath = "/«submodule.moduleSourcePath.replace(java.io.File.separatorChar, '/')»";
+
+ private final «Set.importedName»<YangModuleInfo> importedModules;
+
+ public static «YangModuleInfo.importedName» getInstance() {
+ return INSTANCE;
+ }
+
+ «classBody(submodule, BindingMapping.getClassName(submodule.name + "Info"))»
+ }
+ «ENDFOR»
+ '''
+
}
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware {
private static final String FS = File.separator;
private BuildContext buildContext;
outputBaseDir = outputDir == null ? getDefaultOutputBaseDir() : outputDir;
- final BindingGenerator bindingGenerator = new BindingGeneratorImpl();
+ final BindingGenerator bindingGenerator = new BindingGeneratorImpl(true);
final List<Type> types = bindingGenerator.generateTypes(context, yangModules);
final GeneratorJavaFile generator = new GeneratorJavaFile(buildContext, types);
return result;
}
- private Collection<? extends File> generateModuleInfos(File outputBaseDir, Set<Module> yangModules,
- SchemaContext context) {
+ private Collection<? extends File> generateModuleInfos(final File outputBaseDir, final Set<Module> yangModules,
+ final SchemaContext context) {
Builder<File> result = ImmutableSet.builder();
Builder<String> bindingProviders = ImmutableSet.builder();
for (Module module : yangModules) {
return result.build();
}
- private File writeMetaInfServices(File outputBaseDir, Class<YangModelBindingProvider> serviceClass,
- ImmutableSet<String> services) {
+ private File writeMetaInfServices(final File outputBaseDir, final Class<YangModelBindingProvider> serviceClass,
+ final ImmutableSet<String> services) {
File metainfServicesFolder = new File(outputBaseDir, "META-INF" + File.separator + "services");
metainfServicesFolder.mkdirs();
File serviceFile = new File(metainfServicesFolder, serviceClass.getName());
return outputBaseDir;
}
- private static void setOutputBaseDirAsSourceFolder(File outputBaseDir, MavenProject mavenProject) {
+ private static void setOutputBaseDirAsSourceFolder(final File outputBaseDir, final MavenProject mavenProject) {
Preconditions.checkNotNull(mavenProject, "Maven project needs to be set in this phase");
mavenProject.addCompileSourceRoot(outputBaseDir.getPath());
}
@Override
- public void setLog(Log log) {
+ public void setLog(final Log log) {
}
@Override
- public void setAdditionalConfig(Map<String, String> additionalConfiguration) {
+ public void setAdditionalConfig(final Map<String, String> additionalConfiguration) {
this.additionalConfig = additionalConfiguration;
}
@Override
- public void setResourceBaseDir(File resourceBaseDir) {
+ public void setResourceBaseDir(final File resourceBaseDir) {
this.resourceBaseDir = resourceBaseDir;
}
@Override
- public void setMavenProject(MavenProject project) {
+ public void setMavenProject(final MavenProject project) {
this.mavenProject = project;
this.projectBaseDir = project.getBasedir();
}
@Override
- public void setBuildContext(BuildContext buildContext) {
+ public void setBuildContext(final BuildContext buildContext) {
this.buildContext = Preconditions.checkNotNull(buildContext);
}
- private Set<File> generateYangModuleInfo(File outputBaseDir, Module module, SchemaContext ctx,
- Builder<String> providerSourceSet) {
+ private Set<File> generateYangModuleInfo(final File outputBaseDir, final Module module, final SchemaContext ctx,
+ final Builder<String> providerSourceSet) {
Builder<File> generatedFiles = ImmutableSet.<File> builder();
final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, ctx);
}
- private File writeJavaSource(File packageDir, String className, String source) {
+ private File writeJavaSource(final File packageDir, final String className, final String source) {
if (!packageDir.exists()) {
packageDir.mkdirs();
}
return file;
}
- private File writeFile(File file, String source) {
+ private File writeFile(final File file, final String source) {
try (final OutputStream stream = buildContext.newFileOutputStream(file)) {
try (final Writer fw = new OutputStreamWriter(stream)) {
try (final BufferedWriter bw = new BufferedWriter(fw)) {
--- /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.unified.doc.generator.maven;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.sonatype.plexus.build.incremental.DefaultBuildContext;
+
+/**
+ * Test correct generation of YangModuleInfo class.
+ *
+ */
+// TODO: most of private static methods are copied from
+// binding-java-api-generator project - reorganize compilation tests
+public class YangModuleInfoCompilationTest {
+ public static final String FS = File.separator;
+ private static final String BASE_PKG = "org.opendaylight.yang.gen.v1";
+
+ private static final String TEST_PATH = "target" + FS + "test";
+ private static final File TEST_DIR = new File(TEST_PATH);
+
+ private static final String GENERATOR_OUTPUT_PATH = TEST_PATH + FS + "src";
+ private static final File GENERATOR_OUTPUT_DIR = new File(GENERATOR_OUTPUT_PATH);
+ private static final String COMPILER_OUTPUT_PATH = TEST_PATH + FS + "bin";
+ private static final File COMPILER_OUTPUT_DIR = new File(COMPILER_OUTPUT_PATH);
+
+ @BeforeClass
+ public static void createTestDirs() {
+ if (TEST_DIR.exists()) {
+ deleteTestDir(TEST_DIR);
+ }
+ assertTrue(GENERATOR_OUTPUT_DIR.mkdirs());
+ assertTrue(COMPILER_OUTPUT_DIR.mkdirs());
+ }
+
+ @Test
+ public void compilationTest() throws Exception {
+ final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "yang");
+ assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdirs());
+ final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "yang");
+ assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdirs());
+
+ generateTestSources("/yang-module-info", sourcesOutputDir);
+
+ // Test if $YangModuleInfoImpl.java file is generated
+ final String BASE_PATH = "org" + FS + "opendaylight" + FS + "yang" + FS + "gen" + FS + "v1";
+ final String NS_TEST = BASE_PATH + FS + "yang" + FS + "test" + FS + "main" + FS + "rev140630";
+ File parent = new File(sourcesOutputDir, NS_TEST);
+ File keyArgs = new File(parent, "$YangModuleInfoImpl.java");
+ assertTrue(keyArgs.exists());
+
+ // Test if sources are compilable
+ testCompilation(sourcesOutputDir, compiledOutputDir);
+
+ // Create URLClassLoader
+ File[] roots = File.listRoots();
+ URL[] urls = new URL[roots.length + 1];
+ for (int i = 0; i < roots.length; i++) {
+ urls[i] = roots[i].toURI().toURL();
+
+ }
+ urls[roots.length] = compiledOutputDir.toURI().toURL();
+ ClassLoader loader = new URLClassLoader(urls);
+
+ // Load class
+ Class<?> yangModuleInfoClass = Class.forName(BASE_PKG + ".yang.test.main.rev140630.$YangModuleInfoImpl", true,
+ loader);
+
+ // Test generated $YangModuleInfoImpl class
+ assertFalse(yangModuleInfoClass.isInterface());
+ Method getInstance = assertContainsMethod(yangModuleInfoClass, YangModuleInfo.class, "getInstance");
+ Object yangModuleInfo = getInstance.invoke(null);
+
+ // Test getImportedModules method
+ Method getImportedModules = assertContainsMethod(yangModuleInfoClass, Set.class, "getImportedModules");
+ Object importedModules = getImportedModules.invoke(yangModuleInfo);
+ assertTrue(importedModules instanceof Set);
+
+ YangModuleInfo infoImport = null;
+ YangModuleInfo infoSub1 = null;
+ YangModuleInfo infoSub2 = null;
+ YangModuleInfo infoSub3 = null;
+ for (Object importedModule : (Set<?>) importedModules) {
+ assertTrue(importedModule instanceof YangModuleInfo);
+ YangModuleInfo ymi = (YangModuleInfo) importedModule;
+ String name = ymi.getName();
+
+ switch (name) {
+ case "import-module":
+ infoImport = ymi;
+ break;
+ case "submodule1":
+ infoSub1 = ymi;
+ break;
+ case "submodule2":
+ infoSub2 = ymi;
+ break;
+ case "submodule3":
+ infoSub3 = ymi;
+ }
+ }
+ assertNotNull(infoImport);
+ assertNotNull(infoSub1);
+ assertNotNull(infoSub2);
+ assertNotNull(infoSub3);
+
+ cleanUp(sourcesOutputDir, compiledOutputDir);
+ }
+
+ private void generateTestSources(String resourceDirPath, File sourcesOutputDir) throws Exception {
+ final List<File> sourceFiles = getSourceFiles(resourceDirPath);
+ YangContextParser parser = new YangParserImpl();
+ final SchemaContext context = parser.parseFiles(sourceFiles);
+ CodeGeneratorImpl codegen = new CodeGeneratorImpl();
+ codegen.setBuildContext(new DefaultBuildContext());
+ codegen.generateSources(context, sourcesOutputDir, context.getModules());
+ }
+
+ private static void testCompilation(File sourcesOutputDir, File compiledOutputDir) {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+ List<File> filesList = getJavaFiles(sourcesOutputDir);
+ Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
+ Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
+ boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
+ assertTrue(compiled);
+ }
+
+ private static List<File> getJavaFiles(File directory) {
+ List<File> result = new ArrayList<>();
+ File[] filesToRead = directory.listFiles();
+ if (filesToRead != null) {
+ for (File file : filesToRead) {
+ if (file.isDirectory()) {
+ result.addAll(getJavaFiles(file));
+ } else {
+ String absPath = file.getAbsolutePath();
+ if (absPath.endsWith(".java")) {
+ result.add(file);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List<File> getSourceFiles(String path) throws Exception {
+ final URI resPath = YangModuleInfoCompilationTest.class.getResource(path).toURI();
+ final File sourcesDir = new File(resPath);
+ if (sourcesDir.exists()) {
+ final List<File> sourceFiles = new ArrayList<>();
+ final File[] fileArray = sourcesDir.listFiles();
+ if (fileArray == null) {
+ throw new IllegalArgumentException("Unable to locate files in " + sourcesDir);
+ }
+ sourceFiles.addAll(Arrays.asList(fileArray));
+ return sourceFiles;
+ } else {
+ throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")");
+ }
+ }
+
+ private static void deleteTestDir(File file) {
+ if (file.isDirectory()) {
+ File[] filesToDelete = file.listFiles();
+ if (filesToDelete != null) {
+ for (File f : filesToDelete) {
+ deleteTestDir(f);
+ }
+ }
+ }
+ if (!file.delete()) {
+ throw new RuntimeException("Failed to clean up after test");
+ }
+ }
+
+ private static Method assertContainsMethod(Class<?> clazz, Class<?> returnType, String name, Class<?>... args) {
+ try {
+ Method m = clazz.getDeclaredMethod(name, args);
+ assertEquals(returnType, m.getReturnType());
+ return m;
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError("Method " + name + " with args " + Arrays.toString(args)
+ + " does not exists in class " + clazz.getSimpleName());
+ }
+ }
+
+ private static void cleanUp(File... resourceDirs) {
+ for (File resourceDir : resourceDirs) {
+ if (resourceDir.exists()) {
+ deleteTestDir(resourceDir);
+ }
+ }
+ }
+
+}
--- /dev/null
+module import-module {
+ yang-version 1;
+ namespace "yang:test:import";
+ prefix "i";
+
+ revision "2013-11-19" {
+ }
+
+
+ container imp-cont {}
+
+}
--- /dev/null
+module main-module {
+
+ namespace "yang:test:main";
+ prefix m;
+
+ include submodule1 {
+ revision-date 2014-04-02;
+ }
+
+ include submodule2 {
+ revision-date 2014-06-30;
+ }
+
+ include submodule3 {
+ revision-date 2014-06-30;
+ }
+
+ import import-module {
+ prefix n;
+ }
+
+ revision 2014-06-30 {
+ }
+
+}
--- /dev/null
+submodule submodule1 {
+
+ belongs-to main-module {
+ prefix m;
+ }
+
+ revision 2014-04-02 {
+ }
+
+
+ typedef sub1-type {
+ type string;
+ }
+
+ container sub1-cont {}
+
+}
--- /dev/null
+submodule submodule2 {
+
+ belongs-to main-module {
+ prefix m;
+ }
+
+ include submodule1 {
+ revision-date 2014-04-02;
+ }
+
+ revision 2014-06-30 {
+ }
+
+
+ grouping sub2-gr {
+ leaf sub2-leaf {
+ type string;
+ }
+ }
+
+}
--- /dev/null
+submodule submodule3 {
+
+ belongs-to main-module {
+ prefix m;
+ }
+
+ revision 2014-06-30 {
+ }
+
+
+ container sub3-cont {}
+
+}
@Override
public boolean equals( Object obj ) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof ListenerKey<?>)) {
+ return false;
+ }
+
ListenerKey<?> other = (ListenerKey<?>) obj;
return listener == other.listener;
}
<module>yang-data-util</module>
<module>yang-data-impl</module>
<module>yang-data-operations</module>
+ <module>yang-data-codec-gson</module>
<module>yang-model-api</module>
<module>yang-maven-plugin</module>
<module>yang-maven-plugin-it</module>
*/
package org.opendaylight.yangtools.yang.binding;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
/**
* Event Stream Writer for Binding Representation
* and finished using {@link #endNode()}.</li>
*
* <li><code>UnkeyedList</code> - Unkeyed list represent list without keys,
- * unkeyed list start is emmited using {@link #startUnkeyedList(Class, int)} list
- * end is emmited using {@link #endNode()}. Each list item is emmited using
+ * unkeyed list start is emitted using {@link #startUnkeyedList(Class, int)} list
+ * end is emitted using {@link #endNode()}. Each list item is emitted using
* {@link #startUnkeyedListItem()} and ended using {@link #endNode()}.</li>
* </ul>
*
* <li><code>leaf</code> - Leaf node event is emitted using
- * {@link #leafNode(String, Object)}. {@link #endNode()} MUST be not emmited for
+ * {@link #leafNode(String, Object)}. {@link #endNode()} MUST be not emitted for
* leaf node.</li>
*
* <li><code>leaf-list</code> - Leaf list start is emitted using
* {@link #startLeafSet(String, int)}. Leaf list end is emitted using
- * {@link #endNode()}. Leaf list entries are emmited using
+ * {@link #endNode()}. Leaf list entries are emitted using
* {@link #leafSetEntryNode(Object).
*
* <li><code>anyxml - Anyxml node event is emitted using
- * {@link #leafNode(String, Object)}. {@link #endNode()} MUST be not emmited
+ * {@link #leafNode(String, Object)}. {@link #endNode()} MUST be not emitted
* for anyxml node.</code></li>
*
*
- * <li><code>choice</code> Choice node event is emmited by
+ * <li><code>choice</code> Choice node event is emitted by
* {@link #startChoiceNode(Class, int)} event and must be immediately followed by
- * {@link #startCase(Class, int)} event. Choice node is finished by emitting
+ * {@link #startCase(Class, int)} event. Choice node is finished by emitting an
* {@link #endNode()} event.</li>
*
* <li>
* <code>case</code> - Case node may be emitted only inside choice node by
- * invoking {@link #startCase(Class, int)}. Case node is finished be emitting
+ * invoking {@link #startCase(Class, int)}. Case node is finished be emitting an
* {@link #endNode()} event.</li>
*
* <li>
*
*
*/
-public interface BindingStreamEventWriter {
+public interface BindingStreamEventWriter extends Closeable, Flushable {
/**
* Methods in this interface allow users to hint the underlying
- * implementation about the sizing of container-like constructurs
+ * implementation about the sizing of container-like constructors
* (leafLists, containers, etc.). These hints may be taken into account by a
* particular implementation to improve performance, but clients are not
* required to provide hints. This constant should be used by clients who
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void leafNode(String localName, Object value) throws IllegalArgumentException;
+ void leafNode(String localName, Object value) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startLeafSet(String localName, int childSizeHint) throws IllegalArgumentException;
+ void startLeafSet(String localName, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits a leaf set entry node
* If emitted leaf node has invalid value.
* @throws IllegalStateException
* If node was emitted outside <code>leaf set</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void leafSetEntryNode(Object value) throws IllegalArgumentException;
+ void leafSetEntryNode(Object value) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startContainerNode(Class<? extends DataObject> container, int childSizeHint) throws IllegalArgumentException;
+ void startContainerNode(Class<? extends DataObject> container, int childSizeHint) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startUnkeyedList(Class<? extends DataObject> localName, int childSizeHint) throws IllegalArgumentException;
+ void startUnkeyedList(Class<? extends DataObject> localName, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits start of new unkeyed list item.
* events than count.
* @throws IllegalStateException
* If node was emitted outside <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startUnkeyedListItem(int childSizeHint) throws IllegalStateException;
+ void startUnkeyedListItem(int childSizeHint) throws IOException, IllegalStateException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
<T extends DataObject & Identifiable<?>> void startMapNode(Class<T> mapEntryType, int childSizeHint)
- throws IllegalArgumentException;
-
+ throws IOException, IllegalArgumentException;
/**
- *
- * Emits start of ordered map node event.
- *
- * <p>
- * End of map node event is emitted by invoking {@link #endNode()}. Valid
- * subevents is only {@link #startMapEntryNode(Identifier, int)}. All other methods will
- * throw {@link IllegalArgumentException}.
- *
- * @param mapEntryType
- * Class of list item, which has defined key.
- * @param childSizeHint
- * Non-negative count of expected direct child nodes or
- * {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
- * and should not fail writing of child events, if there are more
- * events than count.
- * @throws IllegalArgumentException
- * @throws IllegalStateException
- * If node was emitted inside <code>map</code>,
- * <code>choice</code> <code>unkeyed list</code> node.
- */
- <T extends DataObject & Identifiable<?>> void startOrderedMapNode(Class<T> mapEntryType, int childSizeHint)
- throws IllegalArgumentException;
+ *
+ * Emits start of ordered map node event.
+ *
+ * <p>
+ * End of map node event is emitted by invoking {@link #endNode()}. Valid
+ * subevents is only {@link #startMapEntryNode(Identifier, int)}. All other methods will
+ * throw {@link IllegalArgumentException}.
+ *
+ * @param mapEntryType
+ * Class of list item, which has defined key.
+ * @param childSizeHint
+ * Non-negative count of expected direct child nodes or
+ * {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+ * and should not fail writing of child events, if there are more
+ * events than count.
+ * @throws IllegalArgumentException
+ * @throws IllegalStateException
+ * If node was emitted inside <code>map</code>,
+ * <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
+ */
+ <T extends DataObject & Identifiable<?>> void startOrderedMapNode(Class<T> mapEntryType, int childSizeHint)
+ throws IOException, IllegalArgumentException;
/**
*
* If key contains incorrect value.
* @throws IllegalStateException
* If node was emitted outside <code>map entry</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startMapEntryNode(Identifier<?> keyValues, int childSizeHint) throws IllegalArgumentException;
+ void startMapEntryNode(Identifier<?> keyValues, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits start of choice node.
* @throws IllegalStateException
* If node was emitted inside <code>map</code>, <code>choice
* </code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startChoiceNode(Class<? extends DataContainer> choice, int childSizeHint) throws IllegalArgumentException;
+ void startChoiceNode(Class<? extends DataContainer> choice, int childSizeHint) throws IOException, IllegalArgumentException;
/**
*
*
* @param name
* @throws IllegalArgumentException
+ * @throws IOException if an underlying IO error occurs
*/
- void startCase(Class<? extends DataObject> caze, int childSizeHint) throws IllegalArgumentException;
+ void startCase(Class<? extends DataObject> caze, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits start of augmentation node.
* Local names of all valid children defined by augmentation.
* @throws IllegalArgumentException
* If augmentation is invalid in current context.
+ * @throws IOException if an underlying IO error occurs
*/
- void startAugmentationNode(Class<? extends Augmentation<?>> augmentationType) throws IllegalArgumentException;
+ void startAugmentationNode(Class<? extends Augmentation<?>> augmentationType) throws IOException, IllegalArgumentException;
/**
* Emits anyxml node event.
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void anyxmlNode(String name, Object value) throws IllegalArgumentException;
+ void anyxmlNode(String name, Object value) throws IOException, IllegalArgumentException;
/**
* Emits end event for node.
*
* @throws IllegalStateException If there is no open node.
+ * @throws IOException if an underlying IO error occurs
*/
- void endNode() throws IllegalStateException;
+ void endNode() throws IOException, IllegalStateException;
+
+ @Override
+ void flush() throws IOException;
+
+ @Override
+ void close() throws IOException;
}
*/
package org.opendaylight.yangtools.yang.binding;
+import java.io.IOException;
+
/**
- *
- * Serializer which writes DataObject to supplied stream event writer.
- *
- *
+ * A serializer which writes DataObject to supplied stream event writer.
*/
public interface DataObjectSerializer {
-
/**
- *
* Writes stream events representing object to supplied stream
-
*
* @param obj
* Source of stream events
* @param stream
* Stream to which events should be written.
*/
- void serialize(DataObject obj, BindingStreamEventWriter stream);
-
+ void serialize(DataObject obj, BindingStreamEventWriter stream) throws IOException;
}
*/
package org.opendaylight.yangtools.yang.binding;
+import java.io.IOException;
+
/**
* SPI-level contract for implementations of {@link DataObjectSerializer}.
* The contract is kept between implementation of {@link DataObjectSerializerRegistry},
*
* FIXME: this interface needs to be moved into .spi, but due to classpath funkyness
* of OSGi, that change has to be carefully orchestrated to ensure proper imports
- * exist in all generated pacakges. One avenue how to achieve that is to move
+ * exist in all generated packages. One avenue how to achieve that is to move
* {@link YangModuleInfo} and modify code generator to add a static field
* to all generated classes which will point to the per-model YangModuleInfo
* (currently all users of it have to walk the package hierarchy, so that
*
*/
public interface DataObjectSerializerImplementation {
-
/**
*
* Writes stream events for supplied data object to provided stream.
* their events.
*
*/
- void serialize(DataObjectSerializerRegistry reg,DataObject obj, BindingStreamEventWriter stream);
-
+ void serialize(DataObjectSerializerRegistry reg,DataObject obj, BindingStreamEventWriter stream) throws IOException;
}
--- /dev/null
+package org.opendaylight.yangtools.yang.data.api.schema.stream;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public class LoggingNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(LoggingNormalizedNodeStreamWriter.class);
+ private static final int DEFAULT_INDENT_SIZE = 2;
+ private final int indentSize = DEFAULT_INDENT_SIZE;
+ private int currentIndent = 0;
+
+ private String ind() {
+ return Strings.repeat(" ", currentIndent);
+ }
+
+ private void decIndent() {
+ Preconditions.checkState(currentIndent >= 0, "Unexpected indentation %s", currentIndent);
+ currentIndent -= indentSize;
+ }
+
+ private void incIndent() {
+ currentIndent += indentSize;
+ }
+
+ @Override
+ public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IllegalStateException {
+ LOG.debug("{}{}[](no key)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(no key)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+
+ }
+
+ @Override
+ public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(key)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IllegalArgumentException {
+ LOG.debug("{}{}[](key)", ind(), identifier);
+ incIndent();
+ }
+
+ @Override
+ public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(leaf-list)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(container)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(choice)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+ LOG.debug("{}{}(augmentation)", ind(), identifier);
+ incIndent();
+ }
+
+ @Override
+ public void leafSetEntryNode(final Object value) throws IllegalArgumentException {
+ LOG.debug("{}{}({}) ", ind(), value, value.getClass().getSimpleName());
+ }
+
+ @Override
+ public void leafNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+ LOG.debug("{}{}(leaf({}))=", ind(), name, value.getClass().getSimpleName(), value);
+ }
+
+ @Override
+ public void endNode() throws IllegalStateException {
+ decIndent();
+ LOG.debug("{}(end)", ind());
+ }
+
+ @Override
+ public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+ LOG.debug("{}{}(anyxml)=", ind(), name, value);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ LOG.trace("<<FLUSH>>");
+ }
+
+ @Override
+ public void close() throws IOException {
+ LOG.debug("<<END-OF-STREAM>>");
+ }
+}
\ No newline at end of file
*/
package org.opendaylight.yangtools.yang.data.api.schema.stream;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
* and finished using {@link #endNode()}.</li>
*
* <li><code>UnkeyedList</code> - Unkeyed list represent list without keys,
- * unkeyed list start is emmited using {@link #startUnkeyedList(NodeIdentifier, int)} list
- * end is emmited using {@link #endNode()}. Each list item is emmited using
+ * unkeyed list start is emitted using {@link #startUnkeyedList(NodeIdentifier, int)} list
+ * end is emitted using {@link #endNode()}. Each list item is emitted using
* {@link #startUnkeyedListItem(NodeIdentifier, int)} and ended using {@link #endNode()}.</li>
* </ul>
*
* <li><code>leaf</code> - Leaf node event is emitted using
- * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emmited for
+ * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emitted for
* leaf node.</li>
*
* <li><code>leaf-list</code> - Leaf list start is emitted using
* {@link #leafSetEntryNode(Object).
*
* <li><code>anyxml - Anyxml node event is emitted using
- * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emmited
+ * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emitted
* for anyxml node.</code></li>
*
*
* and resources needlessly.
*
*/
-public interface NormalizedNodeStreamWriter {
+public interface NormalizedNodeStreamWriter extends Closeable, Flushable {
/**
* Methods in this interface allow users to hint the underlying
- * implementation about the sizing of container-like constructurs
+ * implementation about the sizing of container-like constructors
* (leafLists, containers, etc.). These hints may be taken into account by a
* particular implementation to improve performance, but clients are not
* required to provide hints. This constant should be used by clients who
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void leafNode(NodeIdentifier name, Object value) throws IllegalArgumentException;
+ void leafNode(NodeIdentifier name, Object value) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startLeafSet(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+ void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits a leaf set entry node
* If emitted leaf node has invalid value.
* @throws IllegalStateException
* If node was emitted outside <code>leaf set</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void leafSetEntryNode(Object value) throws IllegalArgumentException;
+ void leafSetEntryNode(Object value) throws IOException, IllegalArgumentException;
/**
*
* <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
* <li>{@link #startMapNode(NodeIdentifier, int)}</li>
* <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
- * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
- * </ul>
+ * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+ * </ul>
*
* @param name
* name of node as defined in schema, namespace and revision are
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startContainerNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+ void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+ void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits start of new unkeyed list item.
* events than count.
* @throws IllegalStateException
* If node was emitted outside <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IllegalStateException;
+ void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IOException, IllegalStateException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+ void startMapNode(NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException;
/**
*
* If key contains incorrect value.
* @throws IllegalStateException
* If node was emitted outside <code>map entry</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IllegalArgumentException;
+ void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+ void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException;
/**
*
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+ void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException;
/**
* Emits start of augmentation node.
* Augmentation identifier
* @throws IllegalArgumentException
* If augmentation is invalid in current context.
+ * @throws IOException if an underlying IO error occurs
*/
- void startAugmentationNode(AugmentationIdentifier identifier) throws IllegalArgumentException;
+ void startAugmentationNode(AugmentationIdentifier identifier) throws IOException, IllegalArgumentException;
/**
* Emits anyxml node event.
*
- *
* @param name
* @param value
* @throws IllegalArgumentException
* @throws IllegalStateException
* If node was emitted inside <code>map</code>,
* <code>choice</code> <code>unkeyed list</code> node.
+ * @throws IOException if an underlying IO error occurs
*/
- void anyxmlNode(NodeIdentifier name, Object value) throws IllegalArgumentException;
+ void anyxmlNode(NodeIdentifier name, Object value) throws IOException, IllegalArgumentException;
/**
* Emits end event for node.
*
- * @throws IllegalStateException If there is no start* event to be closed.B
- *
+ * @throws IllegalStateException If there is no start* event to be closed.
+ * @throws IOException if an underlying IO error occurs
*/
- void endNode() throws IllegalStateException;
+ void endNode() throws IOException, IllegalStateException;
+
+ @Override
+ void close() throws IOException;
+ @Override
+ void flush() throws IOException;
}
--- /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.stream;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
+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.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+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;
+
+/**
+ * This is an experimental
+ */
+@Beta
+public final class NormalizedNodeWriter implements Closeable, Flushable {
+ private final NormalizedNodeStreamWriter writer;
+
+ private NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+ this.writer = Preconditions.checkNotNull(writer);
+ }
+
+ public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+ return new NormalizedNodeWriter(writer);
+ }
+
+ public NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+ if (wasProcessedAsCompositeNode(node)) {
+ return this;
+ }
+
+ if (wasProcessAsSimpleNode(node)) {
+ return this;
+ }
+
+ throw new IllegalStateException("It wasn't possible to serialize node " + node);
+ }
+
+ private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+ if (node instanceof LeafSetEntryNode) {
+ final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
+ writer.leafSetEntryNode(nodeAsLeafList.getValue());
+ return true;
+ } else if (node instanceof LeafNode) {
+ final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+ writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+ return true;
+ } else if (node instanceof AnyXmlNode) {
+ final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+ writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+ return true;
+ }
+
+ return false;
+ }
+
+ 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;
+ }
+ return false;
+
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.close();
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yangtools-parent</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ <relativePath>/../../common/parent/pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>yang-data-codec-gson</artifactId>
+ <name>${project.artifactId}</name>
+ <description>${project.artifactId}</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractNodeDataWithSchema {
+
+ private final DataSchemaNode schema;
+
+ public AbstractNodeDataWithSchema(final DataSchemaNode schema) {
+ this.schema = schema;
+ }
+
+ public final DataSchemaNode getSchema() {
+ return schema;
+ }
+
+ protected abstract void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException;
+
+ protected NodeIdentifier provideNodeIdentifier() {
+ return new NodeIdentifier(schema.getQName());
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((schema == null) ? 0 : schema.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AbstractNodeDataWithSchema other = (AbstractNodeDataWithSchema) obj;
+ if (schema == null) {
+ if (other.schema != null) {
+ return false;
+ }
+ } else if (!schema.equals(other.schema)) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
--- /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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema {
+
+ public AnyXmlNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+ super(dataSchemaNode);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+// FIXME: should be changed according to format of value
+ nnStreamWriter.anyxmlNode(provideNodeIdentifier(), getValue());
+ }
+}
--- /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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+
+class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ public CaseNodeDataWithSchema(final ChoiceCaseNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ super.writeToStream(nnStreamWriter);
+ }
+}
--- /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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ *
+ * childs - empty augment - only one element can be
+ *
+ */
+class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ private CaseNodeDataWithSchema caseNodeDataWithSchema;
+
+ public ChoiceNodeDataWithSchema(final ChoiceNode schema) {
+ super(schema);
+ }
+
+ @Override
+ public CompositeNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+ CaseNodeDataWithSchema newChild = new CaseNodeDataWithSchema((ChoiceCaseNode) schema);
+ caseNodeDataWithSchema = newChild;
+ addCompositeChild(newChild);
+ return newChild;
+ }
+
+ public CaseNodeDataWithSchema getCase() {
+ return caseNodeDataWithSchema;
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.startChoiceNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+
+}
--- /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.codec.gson;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
+
+ /**
+ * nodes which were added to schema via augmentation and are present in data input
+ */
+ protected Map<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationsToChild = new HashMap<>();
+
+ /**
+ * remaining data nodes (which aren't added via augment). Every of them should have the same QName
+ */
+ protected List<AbstractNodeDataWithSchema> childs = new ArrayList<>();
+
+ public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ public AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
+ SimpleNodeDataWithSchema newChild = null;
+ if (schema instanceof LeafSchemaNode) {
+ newChild = new LeafNodeDataWithSchema(schema);
+ } else if (schema instanceof AnyXmlSchemaNode) {
+ newChild = new AnyXmlNodeDataWithSchema(schema);
+ }
+
+ if (newChild != null) {
+
+ AugmentationSchema augSchema = null;
+ if (schema.isAugmenting()) {
+ augSchema = findCorrespondingAugment(getSchema(), schema);
+ }
+ if (augSchema != null) {
+ addChildToAugmentation(augSchema, newChild);
+ } else {
+ addChild(newChild);
+ }
+ return newChild;
+ }
+ return null;
+ }
+
+ private void addChildToAugmentation(final AugmentationSchema augSchema, final AbstractNodeDataWithSchema newChild) {
+ List<AbstractNodeDataWithSchema> childsInAugment = augmentationsToChild.get(augSchema);
+ if (childsInAugment == null) {
+ childsInAugment = new ArrayList<>();
+ augmentationsToChild.put(augSchema, childsInAugment);
+ }
+ childsInAugment.add(newChild);
+ }
+
+ public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
+ if (schemas.size() == 1) {
+ final DataSchemaNode childDataSchemaNode = schemas.pop();
+ return addChild(childDataSchemaNode);
+ } else {
+ DataSchemaNode choiceCandidate = schemas.pop();
+ DataSchemaNode caseCandidate = schemas.pop();
+ ChoiceNode choiceNode = null;
+ ChoiceCaseNode caseNode = null;
+ if (choiceCandidate instanceof ChoiceNode) {
+ choiceNode = (ChoiceNode) choiceCandidate;
+ } else {
+ throw new IllegalArgumentException("Awaited node of type ChoiceNode but was "
+ + choiceCandidate.getClass().getSimpleName());
+ }
+
+ if (caseCandidate instanceof ChoiceCaseNode) {
+ caseNode = (ChoiceCaseNode) caseCandidate;
+ } else {
+ throw new IllegalArgumentException("Awaited node of type ChoiceCaseNode but was "
+ + caseCandidate.getClass().getSimpleName());
+ }
+
+ AugmentationSchema augSchema = null;
+ if (choiceCandidate.isAugmenting()) {
+ augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
+ }
+
+ // looking for existing choice
+ List<AbstractNodeDataWithSchema> childNodes = Collections.emptyList();
+ if (augSchema != null) {
+ childNodes = augmentationsToChild.get(augSchema);
+ } else {
+ childNodes = childs;
+ }
+
+ CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
+ if (caseNodeDataWithSchema == null) {
+ ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
+ addChild(choiceNodeDataWithSchema);
+ caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
+ }
+
+ return caseNodeDataWithSchema.addChild(schemas);
+ }
+
+ }
+
+ private CaseNodeDataWithSchema findChoice(final List<AbstractNodeDataWithSchema> childNodes, final DataSchemaNode choiceCandidate,
+ final DataSchemaNode caseCandidate) {
+ if (childNodes == null) {
+ return null;
+ }
+ for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
+ if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
+ && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
+ CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
+ if (casePrevious.getSchema().getQName() != caseCandidate.getQName()) {
+ throw new IllegalArgumentException("Data from case " + caseCandidate.getQName()
+ + " are specified but other data from case " + casePrevious.getSchema().getQName()
+ + " were specified erlier. Data aren't from the same case.");
+ }
+ return casePrevious;
+ }
+ }
+ return null;
+ }
+
+ public AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+ CompositeNodeDataWithSchema newChild;
+ if (schema instanceof ListSchemaNode) {
+ newChild = new ListNodeDataWithSchema(schema);
+ } else if (schema instanceof LeafListSchemaNode) {
+ newChild = new LeafListNodeDataWithSchema(schema);
+ } else if (schema instanceof ContainerSchemaNode) {
+ newChild = new ContainerNodeDataWithSchema(schema);
+ } else {
+ newChild = new CompositeNodeDataWithSchema(schema);
+ }
+ addCompositeChild(newChild);
+ return newChild;
+ }
+
+ public void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
+ AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
+ if (augSchema != null) {
+ addChildToAugmentation(augSchema, newChild);
+ } else {
+ addChild(newChild);
+ }
+ }
+
+ private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
+ AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
+ return newChild == null ? addCompositeChild(schema) : newChild;
+ }
+
+ public void addChild(final AbstractNodeDataWithSchema newChild) {
+ childs.add(newChild);
+ }
+
+ /**
+ * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
+ * node is found then it is returned, else null.
+ */
+ protected AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
+ if (parent instanceof AugmentationTarget) {
+ for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+ DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+ if (childInAugmentation != null) {
+ return augmentation;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ for (AbstractNodeDataWithSchema child : childs) {
+ child.writeToStream(nnStreamWriter);
+ }
+ for (Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.entrySet()) {
+
+ final List<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
+
+ if (!childsFromAgumentation.isEmpty()) {
+ nnStreamWriter.startAugmentationNode(toAugmentationIdentifier(augmentationToChild));
+
+ for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
+ nodeDataWithSchema.writeToStream(nnStreamWriter);
+ }
+
+ nnStreamWriter.endNode();
+ }
+ }
+ }
+
+ private static AugmentationIdentifier toAugmentationIdentifier(
+ final Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild) {
+ Collection<DataSchemaNode> nodes = augmentationToChild.getKey().getChildNodes();
+ Set<QName> nodesQNames = new HashSet<>();
+ for (DataSchemaNode node : nodes) {
+ nodesQNames.add(node.getQName());
+ }
+
+ return new AugmentationIdentifier(nodesQNames);
+ }
+
+}
--- /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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ public ContainerNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.startContainerNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+
+}
--- /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.codec.gson;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This implementation will create JSON output as output stream.
+ *
+ * Values of leaf and leaf-list are NOT translated according to codecs.
+ *
+ * FIXME: rewrite this in terms of {@link JsonWriter}.
+ */
+public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+
+ private static enum NodeType {
+ OBJECT,
+ LIST,
+ OTHER,
+ }
+
+ private static class TypeInfo {
+ private boolean hasAtLeastOneChild = false;
+ private final NodeType type;
+ private final URI uri;
+
+ public TypeInfo(final NodeType type, final URI uri) {
+ this.type = type;
+ this.uri = uri;
+ }
+
+ public void setHasAtLeastOneChild(final boolean hasChildren) {
+ this.hasAtLeastOneChild = hasChildren;
+ }
+
+ public NodeType getType() {
+ return type;
+ }
+
+ public URI getNamespace() {
+ return uri;
+ }
+
+ public boolean hasAtLeastOneChild() {
+ return hasAtLeastOneChild;
+ }
+ }
+
+ private final Deque<TypeInfo> stack = new ArrayDeque<>();
+ private final SchemaContext schemaContext;
+ private final Writer writer;
+ private final String indent;
+
+ private URI currentNamespace = null;
+ private int currentDepth = 0;
+
+ private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext,
+ final Writer writer, final int indentSize) {
+ this.schemaContext = Preconditions.checkNotNull(schemaContext);
+ this.writer = Preconditions.checkNotNull(writer);
+
+ Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative");
+
+ if (indentSize != 0) {
+ indent = Strings.repeat(" ", indentSize);
+ } else {
+ indent = null;
+ }
+ }
+
+ /**
+ * Create a new stream writer, which writes to the specified {@link Writer}.
+ *
+ * @param schemaContext Schema context
+ * @param writer Output writer
+ * @return A stream writer instance
+ */
+ public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer) {
+ return new JSONNormalizedNodeStreamWriter(schemaContext, writer, 0);
+ }
+
+ /**
+ * Create a new stream writer, which writes to the specified output stream.
+ *
+ * @param schemaContext Schema context
+ * @param writer Output writer
+ * @param indentSize indentation size
+ * @return A stream writer instance
+ */
+ public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer, final int indentSize) {
+ return new JSONNormalizedNodeStreamWriter(schemaContext, writer, indentSize);
+ }
+
+ @Override
+ public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
+ separateElementFromPreviousElement();
+ writeJsonIdentifier(name);
+ currentNamespace = stack.peek().getNamespace();
+ writeValue(value.toString());
+ separateNextSiblingsWithComma();
+ }
+
+ @Override
+ public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void leafSetEntryNode(final Object value) throws IOException {
+ separateElementFromPreviousElement();
+ writeValue(value.toString());
+ separateNextSiblingsWithComma();
+ }
+
+ @Override
+ public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartObject();
+ indentRight();
+ }
+
+ @Override
+ public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
+ separateElementFromPreviousElement();
+ writeStartObject();
+ indentRight();
+ }
+
+ @Override
+ public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IOException {
+ stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace()));
+ separateElementFromPreviousElement();
+ writeStartObject();
+ indentRight();
+ }
+
+ @Override
+ public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ separateElementFromPreviousElement();
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ handleInvisibleNode(name.getNodeType().getNamespace());
+ }
+
+ @Override
+ public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+ handleInvisibleNode(currentNamespace);
+ }
+
+ @Override
+ public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
+ separateElementFromPreviousElement();
+ writeJsonIdentifier(name);
+ currentNamespace = stack.peek().getNamespace();
+ writeValue(value.toString());
+ separateNextSiblingsWithComma();
+ }
+
+ @Override
+ public void endNode() throws IOException {
+ switch (stack.peek().getType()) {
+ case LIST:
+ indentLeft();
+ newLine();
+ writer.append(']');
+ break;
+ case OBJECT:
+ indentLeft();
+ newLine();
+ writer.append('}');
+ break;
+ default:
+ break;
+ }
+ stack.pop();
+ currentNamespace = stack.isEmpty() ? null : stack.peek().getNamespace();
+ separateNextSiblingsWithComma();
+ }
+
+ private void separateElementFromPreviousElement() throws IOException {
+ if (!stack.isEmpty() && stack.peek().hasAtLeastOneChild()) {
+ writer.append(',');
+ }
+ newLine();
+ }
+
+ private void newLine() throws IOException {
+ if (indent != null) {
+ writer.append('\n');
+
+ for (int i = 0; i < currentDepth; i++) {
+ writer.append(indent);
+ }
+ }
+ }
+
+ private void separateNextSiblingsWithComma() {
+ if (!stack.isEmpty()) {
+ stack.peek().setHasAtLeastOneChild(true);
+ }
+ }
+
+ /**
+ * Invisible nodes have to be also pushed to stack because of pairing of start*() and endNode() methods. Information
+ * about child existing (due to printing comma) has to be transfered to invisible node.
+ */
+ private void handleInvisibleNode(final URI uri) {
+ TypeInfo typeInfo = new TypeInfo(NodeType.OTHER, uri);
+ typeInfo.setHasAtLeastOneChild(stack.peek().hasAtLeastOneChild());
+ stack.push(typeInfo);
+ }
+
+ private void writeStartObject() throws IOException {
+ writer.append('{');
+ }
+
+ private void writeStartList() throws IOException {
+ writer.append('[');
+ }
+
+ private void writeModulName(final URI namespace) throws IOException {
+ if (this.currentNamespace == null || namespace != this.currentNamespace) {
+ Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+ writer.append(module.getName());
+ writer.append(':');
+ currentNamespace = namespace;
+ }
+ }
+
+ private void writeValue(final String value) throws IOException {
+ writer.append('"');
+ writer.append(value);
+ writer.append('"');
+ }
+
+ private void writeJsonIdentifier(final NodeIdentifier name) throws IOException {
+ writer.append('"');
+ writeModulName(name.getNodeType().getNamespace());
+ writer.append(name.getNodeType().getLocalName());
+ writer.append("\":");
+ }
+
+ private void indentRight() {
+ currentDepth++;
+ }
+
+ private void indentLeft() {
+ currentDepth--;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+}
--- /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.codec.gson;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterators;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.MalformedJsonException;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.Flushable;
+import java.io.IOException;
+import java.net.URI;
+import java.security.InvalidParameterException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil.PrefixMapingFromJson;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.SchemaContextUtils;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+/**
+ * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the
+ * default GSON JsonParser.
+ */
+@Beta
+public final class JsonParserStream implements Closeable, Flushable {
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+
+ private final Deque<URI> namespaces = new ArrayDeque<>();
+ private final NormalizedNodeStreamWriter writer;
+ private final SchemaContextUtils utils;
+ private final RestCodecFactory codecs;
+ private final SchemaContext schema;
+
+ private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
+ this.schema = Preconditions.checkNotNull(schemaContext);
+ this.utils = SchemaContextUtils.create(schemaContext);
+ this.writer = Preconditions.checkNotNull(writer);
+ this.codecs = RestCodecFactory.create(utils);
+ }
+
+ public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
+ return new JsonParserStream(writer, schemaContext);
+ }
+
+ public JsonParserStream parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException {
+ // code copied from gson's JsonParser and Stream classes
+
+ boolean lenient = reader.isLenient();
+ reader.setLenient(true);
+ boolean isEmpty = true;
+ try {
+ reader.peek();
+ isEmpty = false;
+ CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(schema);
+ read(reader, compositeNodeDataWithSchema);
+ compositeNodeDataWithSchema.writeToStream(writer);
+
+ return this;
+ // return read(reader);
+ } catch (EOFException e) {
+ if (isEmpty) {
+ return this;
+ // return JsonNull.INSTANCE;
+ }
+ // The stream ended prematurely so it is likely a syntax error.
+ throw new JsonSyntaxException(e);
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException(e);
+ } catch (StackOverflowError | OutOfMemoryError e) {
+ throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+ } finally {
+ reader.setLenient(lenient);
+ }
+ }
+
+ public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException {
+
+ final JsonToken peek = in.peek();
+ Optional<String> value = Optional.absent();
+ switch (peek) {
+ case STRING:
+ case NUMBER:
+ value = Optional.of(in.nextString());
+ break;
+ case BOOLEAN:
+ value = Optional.of(Boolean.toString(in.nextBoolean()));
+ break;
+ case NULL:
+ in.nextNull();
+ value = Optional.of((String) null);
+ break;
+ default:
+ break;
+ }
+ if (value.isPresent()) {
+ final Object translatedValue = translateValueByType(value.get(), parent.getSchema());
+ ((SimpleNodeDataWithSchema) parent).setValue(translatedValue);
+ }
+
+ switch (peek) {
+ case BEGIN_ARRAY:
+ in.beginArray();
+ while (in.hasNext()) {
+ AbstractNodeDataWithSchema newChild = null;
+ if (parent instanceof ListNodeDataWithSchema) {
+ newChild = new ListEntryNodeDataWithSchema(parent.getSchema());
+ ((CompositeNodeDataWithSchema) parent).addChild(newChild);
+ } else if (parent instanceof LeafListNodeDataWithSchema) {
+ newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema());
+ ((CompositeNodeDataWithSchema) parent).addChild(newChild);
+ }
+ read(in, newChild);
+ }
+ in.endArray();
+ return;
+ case BEGIN_OBJECT:
+ Set<String> namesakes = new HashSet<>();
+ in.beginObject();
+ while (in.hasNext()) {
+ final String jsonElementName = in.nextName();
+ final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName);
+ final String localName = namespaceAndName.getName();
+ addNamespace(namespaceAndName.getUri());
+ if (namesakes.contains(jsonElementName)) {
+ throw new JsonSyntaxException("Duplicate name " + jsonElementName + " in JSON input.");
+ }
+ namesakes.add(jsonElementName);
+ final Deque<DataSchemaNode> childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parent.getSchema(),
+ localName, getCurrentNamespace());
+ if (childDataSchemaNodes.isEmpty()) {
+ throw new IllegalStateException("Schema for node with name " + localName + " and namespace "
+ + getCurrentNamespace() + " doesn't exist.");
+ }
+
+ AbstractNodeDataWithSchema newChild;
+ newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes);
+// FIXME:anyxml data shouldn't be skipped but should be loaded somehow. will be specified after 17AUG2014
+ if (newChild instanceof AnyXmlNodeDataWithSchema) {
+ in.skipValue();
+ } else {
+ read(in, newChild);
+ }
+ removeNamespace();
+ }
+ in.endObject();
+ return;
+ case END_DOCUMENT:
+ case NAME:
+ case END_OBJECT:
+ case END_ARRAY:
+ }
+ }
+
+ private Object translateValueByType(final String value, final DataSchemaNode node) {
+ final TypeDefinition<? extends Object> typeDefinition = typeDefinition(node);
+ if (typeDefinition == null) {
+ return value;
+ }
+
+ final Object inputValue;
+ if (typeDefinition instanceof IdentityrefTypeDefinition) {
+ inputValue = valueAsIdentityRef(value);
+ } else if (typeDefinition instanceof InstanceIdentifierTypeDefinition) {
+ inputValue = valueAsInstanceIdentifier(value);
+ } else {
+ inputValue = value;
+ }
+
+ // FIXME: extract this as a cacheable context?
+ final Codec<Object, Object> codec = codecs.codecFor(typeDefinition);
+ if (codec == null) {
+ return null;
+ }
+ return codec.deserialize(inputValue);
+ }
+
+ private static TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
+ TypeDefinition<?> baseType = null;
+ if (node instanceof LeafListSchemaNode) {
+ baseType = ((LeafListSchemaNode) node).getType();
+ } else if (node instanceof LeafSchemaNode) {
+ baseType = ((LeafSchemaNode) node).getType();
+ } else if (node instanceof AnyXmlSchemaNode) {
+ return null;
+ } else {
+ throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
+ }
+
+ if (baseType != null) {
+ while (baseType.getBaseType() != null) {
+ baseType = baseType.getBaseType();
+ }
+ }
+ return baseType;
+ }
+
+ private static Object valueAsInstanceIdentifier(final String value) {
+ // it could be instance-identifier Built-In Type
+ if (!value.isEmpty() && value.charAt(0) == '/') {
+ IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson());
+ if (resolvedValue != null) {
+ return resolvedValue;
+ }
+ }
+ throw new InvalidParameterException("Value for instance-identifier doesn't have correct format");
+ }
+
+ private static IdentityValuesDTO valueAsIdentityRef(final String value) {
+ // it could be identityref Built-In Type
+ URI namespace = getNamespaceFor(value);
+ if (namespace != null) {
+ return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value);
+ }
+ throw new InvalidParameterException("Value for identityref has to be in format moduleName:localName.");
+ }
+
+ private static URI getNamespaceFor(final String jsonElementName) {
+ final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+ // The string needs to me in form "moduleName:localName"
+ if (it.hasNext()) {
+ final String maybeURI = it.next();
+ if (Iterators.size(it) == 1) {
+ return URI.create(maybeURI);
+ }
+ }
+
+ return null;
+ }
+
+ private static String getLocalNameFor(final String jsonElementName) {
+ final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+ // The string needs to me in form "moduleName:localName"
+ final String ret = Iterators.get(it, 1, null);
+ return ret != null && !it.hasNext() ? ret : jsonElementName;
+ }
+
+ private void removeNamespace() {
+ namespaces.pop();
+ }
+
+ private void addNamespace(final Optional<URI> namespace) {
+ if (!namespace.isPresent()) {
+ if (namespaces.isEmpty()) {
+ throw new IllegalStateException("Namespace has to be specified at top level.");
+ } else {
+ namespaces.push(namespaces.peek());
+ }
+ } else {
+ namespaces.push(namespace.get());
+ }
+ }
+
+ private NamespaceAndName resolveNamespace(final String childName) {
+ int lastIndexOfColon = childName.lastIndexOf(":");
+ String moduleNamePart = null;
+ String nodeNamePart = null;
+ URI namespace = null;
+ if (lastIndexOfColon != -1) {
+ moduleNamePart = childName.substring(0, lastIndexOfColon);
+ nodeNamePart = childName.substring(lastIndexOfColon + 1);
+ namespace = utils.findNamespaceByModuleName(moduleNamePart);
+ } else {
+ nodeNamePart = childName;
+ }
+
+ Optional<URI> namespaceOpt = namespace == null ? Optional.<URI> absent() : Optional.of(namespace);
+ return new NamespaceAndName(nodeNamePart, namespaceOpt);
+ }
+
+ private URI getCurrentNamespace() {
+ return namespaces.peek();
+ }
+
+ /**
+ * Returns stack of schema nodes via which it was necessary to prass to get schema node with specified
+ * {@code childName} and {@code namespace}
+ *
+ * @param dataSchemaNode
+ * @param childName
+ * @param namespace
+ * @return stack of schema nodes via which it was passed through. If found schema node is dirrect child then stack
+ * contains only one node. If it is found under choice and case then stack should conains 2*n+1 element
+ * (where n is number of choices through it was passed)
+ */
+ private Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
+ final String childName, final URI namespace) {
+ final Deque<DataSchemaNode> result = new ArrayDeque<>();
+ List<ChoiceNode> childChoices = new ArrayList<>();
+ if (dataSchemaNode instanceof DataNodeContainer) {
+ for (DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+ if (childNode instanceof ChoiceNode) {
+ childChoices.add((ChoiceNode) childNode);
+ } else {
+ final QName childQName = childNode.getQName();
+ if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) {
+ result.push(childNode);
+ return result;
+ }
+ }
+ }
+ }
+ // try to find data schema node in choice (looking for first match)
+ for (ChoiceNode choiceNode : childChoices) {
+ for (ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+ Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName,
+ namespace);
+ if (!resultFromRecursion.isEmpty()) {
+ resultFromRecursion.push(concreteCase);
+ resultFromRecursion.push(choiceNode);
+ return resultFromRecursion;
+ }
+ }
+ }
+ return result;
+ }
+
+ private static class NamespaceAndName {
+ private final Optional<URI> uri;
+ private final String name;
+
+ public NamespaceAndName(final String name, final Optional<URI> uri) {
+ this.name = name;
+ this.uri = uri;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Optional<URI> getUri() {
+ return uri;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+}
--- /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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema {
+ public LeafListEntryNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+ super(dataSchemaNode);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.leafSetEntryNode(getValue());
+ }
+}
--- /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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema {
+ public LeafListNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.startLeafSet(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+}
--- /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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema {
+
+ public LeafNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.leafNode(provideNodeIdentifier(), getValue());
+ }
+
+}
--- /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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ private final Map<QName, SimpleNodeDataWithSchema> qNameToKeys = new HashMap<>();
+
+ public ListEntryNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ public void addChild(final AbstractNodeDataWithSchema newChild) {
+ DataSchemaNode childSchema = newChild.getSchema();
+ if (childSchema instanceof LeafSchemaNode && isPartOfKey((LeafSchemaNode) childSchema)) {
+ qNameToKeys.put(childSchema.getQName(), (SimpleNodeDataWithSchema)newChild);
+ }
+ super.addChild(newChild);
+ }
+
+ private boolean isPartOfKey(final LeafSchemaNode potentialKey) {
+ List<QName> keys = ((ListSchemaNode) getSchema()).getKeyDefinition();
+ for (QName qName : keys) {
+ if (qName.equals(potentialKey.getQName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ int keyCount = ((ListSchemaNode) getSchema()).getKeyDefinition().size();
+ if (keyCount == 0) {
+ nnStreamWriter.startUnkeyedListItem(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ } else if (keyCount == qNameToKeys.size()) {
+ nnStreamWriter.startMapEntryNode(provideNodeIdentifierWithPredicates(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ } else {
+ throw new IllegalStateException("Some of keys of " + getSchema().getQName() + " are missing in input.");
+ }
+ }
+
+ private NodeIdentifierWithPredicates provideNodeIdentifierWithPredicates() {
+ Map<QName, Object> qNameToPredicateValues = new HashMap<>();
+
+ for (SimpleNodeDataWithSchema simpleNodeDataWithSchema : qNameToKeys.values()) {
+ qNameToPredicateValues.put(simpleNodeDataWithSchema.getSchema().getQName(), simpleNodeDataWithSchema.getValue());
+ }
+
+ return new NodeIdentifierWithPredicates(getSchema().getQName(), qNameToPredicateValues);
+ }
+
+}
--- /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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class ListNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ public ListNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ if (!((ListSchemaNode) getSchema()).getKeyDefinition().isEmpty()) {
+ nnStreamWriter.startMapNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+ } else {
+ nnStreamWriter.startUnkeyedList(provideNodeIdentifier(), UNKNOWN_SIZE);
+ }
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+
+}
--- /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.codec.gson;
+
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class SimpleNodeDataWithSchema extends AbstractNodeDataWithSchema {
+
+ private Object value;
+
+ public SimpleNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+ super(dataSchemaNode);
+ }
+
+ void setValue(final Object value) {
+ this.value = value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+}
--- /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.codec.gson.helpers;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractCodecImpl {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecImpl.class);
+ private final SchemaContextUtils schema;
+
+ protected AbstractCodecImpl(final SchemaContextUtils schema) {
+ this.schema = Preconditions.checkNotNull(schema);
+ }
+
+ protected final SchemaContextUtils getSchema() {
+ return schema;
+ }
+
+ protected final Module getModuleByNamespace(final String namespace) {
+ URI validNamespace = resolveValidNamespace(namespace);
+
+ Module module = schema.findModuleByNamespace(validNamespace);
+ if (module == null) {
+ LOG.info("Module for namespace " + validNamespace + " wasn't found.");
+ return null;
+ }
+ return module;
+ }
+
+ protected final URI resolveValidNamespace(final String namespace) {
+ URI validNamespace = schema.findNamespaceByModuleName(namespace);
+ if (validNamespace == null) {
+ validNamespace = URI.create(namespace);
+ }
+
+ return validNamespace;
+ }
+}
--- /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.codec.gson.helpers;
+
+import com.google.common.annotations.Beta;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class IdentityValuesDTO {
+
+ private final List<IdentityValue> elementData = new ArrayList<>();
+ private final String originValue;
+
+ public IdentityValuesDTO(final String namespace, final String value, final String prefix, final String originValue) {
+ elementData.add(new IdentityValue(namespace, value, prefix));
+ this.originValue = originValue;
+ }
+
+ public IdentityValuesDTO(final String originValue) {
+ this.originValue = originValue;
+ }
+
+ public IdentityValuesDTO() {
+ originValue = null;
+ }
+
+ public void add(final String namespace, final String value, final String prefix) {
+ elementData.add(new IdentityValue(namespace, value, prefix));
+ }
+
+ public void add(final IdentityValue identityValue) {
+ elementData.add(identityValue);
+ }
+
+ public List<IdentityValue> getValuesWithNamespaces() {
+ return Collections.unmodifiableList(elementData);
+ }
+
+ @Override
+ public String toString() {
+ return elementData.toString();
+ }
+
+ public String getOriginValue() {
+ return originValue;
+ }
+
+ public static final class IdentityValue {
+
+ private final String namespace;
+ private final String value;
+ private final String prefix;
+ private List<Predicate> predicates;
+
+ public IdentityValue(final String namespace, final String value, final String prefix) {
+ this.namespace = namespace;
+ this.value = value;
+ this.prefix = prefix;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public List<Predicate> getPredicates() {
+ if (predicates == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(predicates);
+ }
+
+ public void setPredicates(final List<Predicate> predicates) {
+ this.predicates = predicates;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (namespace != null) {
+ sb.append(namespace);
+ }
+ if (prefix != null) {
+ sb.append("(").append(prefix).append(")");
+ }
+ if (value != null) {
+ sb.append(" - ").append(value);
+ }
+ if (predicates != null && !predicates.isEmpty()) {
+ for (Predicate predicate : predicates) {
+ sb.append("[");
+ predicate.toString();
+ sb.append("]");
+ }
+ }
+ return sb.toString();
+ }
+
+ }
+
+ public static final class Predicate {
+
+ private final IdentityValue name;
+ private final String value;
+
+ public Predicate(final IdentityValue name, final String value) {
+ super();
+ this.name = name;
+ this.value = value;
+ }
+
+ public IdentityValue getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (name != null) {
+ sb.append(name.toString());
+ }
+ if (value != null) {
+ sb.append("=").append(value);
+ }
+ return sb.toString();
+ }
+
+ public boolean isLeafList() {
+ return name == null ? true : false;
+ }
+
+ }
+}
--- /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.codec.gson.helpers;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class IdentityrefCodecImpl extends AbstractCodecImpl implements IdentityrefCodec<IdentityValuesDTO> {
+ private static final Logger LOG = LoggerFactory.getLogger(IdentityrefCodecImpl.class);
+
+ IdentityrefCodecImpl(final SchemaContextUtils schema) {
+ super(schema);
+ }
+
+ @Override
+ public IdentityValuesDTO serialize(final QName data) {
+ return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), data.getPrefix(), null);
+ }
+
+ @Override
+ public QName deserialize(final IdentityValuesDTO data) {
+ IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+ Module module = getModuleByNamespace(valueWithNamespace.getNamespace());
+ if (module == null) {
+ LOG.info("Module was not found for namespace {}", valueWithNamespace.getNamespace());
+ LOG.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace));
+ return null;
+ }
+
+ return QName.create(module.getNamespace(), module.getRevision(), valueWithNamespace.getValue());
+ }
+
+}
\ No newline at end of file
--- /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.codec.gson.helpers;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InstanceIdentifierCodecImpl extends AbstractCodecImpl implements InstanceIdentifierCodec<IdentityValuesDTO> {
+ private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class);
+
+ InstanceIdentifierCodecImpl(final SchemaContextUtils schema) {
+ super(schema);
+ }
+
+ @Override
+ public IdentityValuesDTO serialize(final YangInstanceIdentifier data) {
+ IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO();
+ for (PathArgument pathArgument : data.getPathArguments()) {
+ IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType());
+ if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) {
+ List<Predicate> predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument)
+ .getKeyValues());
+ identityValue.setPredicates(predicates);
+ } else if (pathArgument instanceof NodeWithValue && identityValue != null) {
+ List<Predicate> predicates = new ArrayList<>();
+ String value = String.valueOf(((NodeWithValue) pathArgument).getValue());
+ predicates.add(new Predicate(null, value));
+ identityValue.setPredicates(predicates);
+ }
+ identityValuesDTO.add(identityValue);
+ }
+ return identityValuesDTO;
+ }
+
+ @Override
+ public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) {
+ List<PathArgument> result = new ArrayList<PathArgument>();
+ IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+ Module module = getModuleByNamespace(valueWithNamespace.getNamespace());
+ if (module == null) {
+ LOG.info("Module by namespace '{}' of first node in instance-identiefier was not found.",
+ valueWithNamespace.getNamespace());
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(valueWithNamespace.getValue()));
+ return null;
+ }
+
+ DataNodeContainer parentContainer = module;
+ List<IdentityValue> identities = data.getValuesWithNamespaces();
+ for (int i = 0; i < identities.size(); i++) {
+ IdentityValue identityValue = identities.get(i);
+ URI validNamespace = resolveValidNamespace(identityValue.getNamespace());
+ DataSchemaNode node = getSchema().findInstanceDataChildByNameAndNamespace(
+ parentContainer, identityValue.getValue(), validNamespace);
+ if (node == null) {
+ LOG.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes());
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ QName qName = node.getQName();
+ PathArgument pathArgument = null;
+ if (identityValue.getPredicates().isEmpty()) {
+ pathArgument = new NodeIdentifier(qName);
+ } else {
+ if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry
+ Predicate leafListPredicate = identityValue.getPredicates().get(0);
+ if (!leafListPredicate.isLeafList()) {
+ LOG.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\"");
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ pathArgument = new NodeWithValue(qName, leafListPredicate.getValue());
+ } else if (node instanceof ListSchemaNode) { // predicates are keys of list
+ DataNodeContainer listNode = (DataNodeContainer) node;
+ Map<QName, Object> predicatesMap = new HashMap<>();
+ for (Predicate predicate : identityValue.getPredicates()) {
+ validNamespace = resolveValidNamespace(predicate.getName().getNamespace());
+ DataSchemaNode listKey = getSchema()
+ .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(),
+ validNamespace);
+ predicatesMap.put(listKey.getQName(), predicate.getValue());
+ }
+ pathArgument = new NodeIdentifierWithPredicates(qName, predicatesMap);
+ } else {
+ LOG.info("Node {} is not List or Leaf-list.", node);
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ }
+ result.add(pathArgument);
+ if (i < identities.size() - 1) { // last element in instance-identifier can be other than
+ // DataNodeContainer
+ if (node instanceof DataNodeContainer) {
+ parentContainer = (DataNodeContainer) node;
+ } else {
+ LOG.info("Node {} isn't instance of DataNodeContainer", node);
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ }
+ }
+
+ return result.isEmpty() ? null : YangInstanceIdentifier.create(result);
+ }
+
+ private static List<Predicate> keyValuesToPredicateList(final Map<QName, Object> keyValues) {
+ List<Predicate> result = new ArrayList<>(keyValues.size());
+ for (Map.Entry<QName, Object> e : keyValues.entrySet()) {
+ result.add(new Predicate(qNameToIdentityValue(e.getKey()), String.valueOf(e.getValue())));
+ }
+ return result;
+ }
+
+ private static IdentityValue qNameToIdentityValue(final QName qName) {
+ if (qName != null) {
+ // FIXME: the prefix here is completely arbitrary
+ return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName(), qName.getPrefix());
+ }
+ return null;
+ }
+}
--- /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.codec.gson.helpers;
+
+import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec;
+
+class LeafrefCodecImpl implements LeafrefCodec<String> {
+
+ @Override
+ public String serialize(final Object data) {
+ return String.valueOf(data);
+ }
+
+ @Override
+ public Object deserialize(final String data) {
+ return data;
+ }
+
+}
\ No newline at end of file
--- /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.codec.gson.helpers;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+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.LeafrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+final class ObjectCodec extends AbstractCodecImpl implements Codec<Object, Object> {
+ public static final Codec LEAFREF_DEFAULT_CODEC = new LeafrefCodecImpl();
+ private static final Logger LOG = LoggerFactory.getLogger(RestCodecFactory.class);
+ private final Codec instanceIdentifier;
+ private final Codec identityrefCodec;
+ private final TypeDefinition<?> type;
+
+ ObjectCodec(final SchemaContextUtils schema, final TypeDefinition<?> typeDefinition) {
+ super(schema);
+ type = RestUtil.resolveBaseTypeFrom(typeDefinition);
+ if (type instanceof IdentityrefTypeDefinition) {
+ identityrefCodec = new IdentityrefCodecImpl(schema);
+ } else {
+ identityrefCodec = null;
+ }
+ if (type instanceof InstanceIdentifierTypeDefinition) {
+ instanceIdentifier = new InstanceIdentifierCodecImpl(schema);
+ } else {
+ instanceIdentifier = null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object deserialize(final Object input) {
+ try {
+ if (type instanceof IdentityrefTypeDefinition) {
+ if (input instanceof IdentityValuesDTO) {
+ return identityrefCodec.deserialize(input);
+ }
+ LOG.debug("Value is not instance of IdentityrefTypeDefinition but is {}. Therefore NULL is used as translation of - {}",
+ input == null ? "null" : input.getClass(), String.valueOf(input));
+ return null;
+ } else if (type instanceof LeafrefTypeDefinition) {
+ if (input instanceof IdentityValuesDTO) {
+ return LEAFREF_DEFAULT_CODEC.deserialize(((IdentityValuesDTO) input).getOriginValue());
+ }
+ return LEAFREF_DEFAULT_CODEC.deserialize(input);
+ } else if (type instanceof InstanceIdentifierTypeDefinition) {
+ if (input instanceof IdentityValuesDTO) {
+ return instanceIdentifier.deserialize(input);
+ }
+ LOG.info(
+ "Value is not instance of InstanceIdentifierTypeDefinition but is {}. Therefore NULL is used as translation of - {}",
+ input == null ? "null" : input.getClass(), String.valueOf(input));
+ return null;
+ } else {
+ TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+ .from(type);
+ if (typeAwarecodec != null) {
+ if (input instanceof IdentityValuesDTO) {
+ return typeAwarecodec.deserialize(((IdentityValuesDTO) input).getOriginValue());
+ }
+ return typeAwarecodec.deserialize(String.valueOf(input));
+ } else {
+ LOG.debug("Codec for type \"" + type.getQName().getLocalName()
+ + "\" is not implemented yet.");
+ return null;
+ }
+ }
+ } catch (ClassCastException e) {
+ // TODO remove this catch when everyone use codecs
+ LOG.error("ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+ e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object serialize(final Object input) {
+ try {
+ if (type instanceof IdentityrefTypeDefinition) {
+ return identityrefCodec.serialize(input);
+ } else if (type instanceof LeafrefTypeDefinition) {
+ return LEAFREF_DEFAULT_CODEC.serialize(input);
+ } else if (type instanceof InstanceIdentifierTypeDefinition) {
+ return instanceIdentifier.serialize(input);
+ } else {
+ TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+ .from(type);
+ if (typeAwarecodec != null) {
+ return typeAwarecodec.serialize(input);
+ } else {
+ LOG.debug("Codec for type \"" + type.getQName().getLocalName()
+ + "\" is not implemented yet.");
+ return null;
+ }
+ }
+ } catch (ClassCastException e) { // TODO remove this catch when everyone use codecs
+ LOG.error(
+ "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+ e);
+ return input;
+ }
+ }
+
+}
\ No newline at end of file
--- /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.codec.gson.helpers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class RestCodecFactory {
+ private final SchemaContextUtils utils;
+
+ private RestCodecFactory(final SchemaContextUtils utils) {
+ this.utils = Preconditions.checkNotNull(utils);
+ }
+
+ public static RestCodecFactory create(final SchemaContextUtils utils) {
+ return new RestCodecFactory(utils);
+ }
+
+ public final Codec<Object, Object> codecFor(final TypeDefinition<?> typeDefinition) {
+ // FIXME: implement loadingcache
+ return new ObjectCodec(utils, typeDefinition);
+ }
+}
--- /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.codec.gson.helpers;
+
+
+import com.google.common.annotations.Beta;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.stream.events.StartElement;
+
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class RestUtil {
+
+ // FIXME: BUG-1275: this is code duplicates data.impl.codec
+
+ public static final String SQUOTE = "'";
+ public static final String DQUOTE = "\"";
+ private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
+
+ public final static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
+ TypeDefinition<?> superType = type;
+ while (superType.getBaseType() != null) {
+ superType = superType.getBaseType();
+ }
+ return superType;
+ }
+
+ public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) {
+ String valueTrimmed = value.trim();
+ if (!valueTrimmed.startsWith("/")) {
+ return null;
+ }
+ String[] xPathParts = valueTrimmed.split("/");
+ if (xPathParts.length < 2) { // must be at least "/pr:node"
+ return null;
+ }
+ IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value);
+ for (int i = 1; i < xPathParts.length; i++) {
+ String xPathPartTrimmed = xPathParts[i].trim();
+
+ String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed);
+ IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap);
+ if (identityValue == null) {
+ return null;
+ }
+
+ List<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
+ if (predicates == null) {
+ return null;
+ }
+ identityValue.setPredicates(predicates);
+
+ identityValuesDTO.add(identityValue);
+ }
+ return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO;
+ }
+
+ private static String getIdAndPrefixAsStr(final String pathPart) {
+ int predicateStartIndex = pathPart.indexOf("[");
+ return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+ }
+
+ private static IdentityValue toIdentity(final String xPathPart, final PrefixesMaping prefixMap) {
+ String xPathPartTrimmed = xPathPart.trim();
+ if (xPathPartTrimmed.isEmpty()) {
+ return null;
+ }
+ String[] prefixAndIdentifier = xPathPartTrimmed.split(":");
+ // it is not "prefix:value"
+ if (prefixAndIdentifier.length != 2) {
+ return null;
+ }
+ String prefix = prefixAndIdentifier[0].trim();
+ String identifier = prefixAndIdentifier[1].trim();
+ if (prefix.isEmpty() || identifier.isEmpty()) {
+ return null;
+ }
+ String namespace = prefixMap.getNamespace(prefix);
+ return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix);
+ }
+
+ private static List<Predicate> toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) {
+ List<Predicate> result = new ArrayList<>();
+ List<String> predicates = new ArrayList<>();
+ Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
+ while (matcher.find()) {
+ predicates.add(matcher.group(1).trim());
+ }
+ for (String predicate : predicates) {
+ int indexOfEqualityMark = predicate.indexOf("=");
+ if (indexOfEqualityMark != -1) {
+ String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1));
+ if (predicate.startsWith(".")) { // it is leaf-list
+ if (predicateValue == null) {
+ return null;
+ }
+ result.add(new Predicate(null, predicateValue));
+ } else {
+ IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap);
+ if (identityValue == null || predicateValue == null) {
+ return null;
+ }
+ result.add(new Predicate(identityValue, predicateValue));
+ }
+ }
+ }
+ return result;
+ }
+
+ private static String toPredicateValue(final String predicatedValue) {
+ String predicatedValueTrimmed = predicatedValue.trim();
+ if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
+ && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
+ return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1);
+ }
+ return null;
+ }
+
+ public interface PrefixesMaping {
+ public String getNamespace(String prefix);
+ }
+
+ public static class PrefixMapingFromXml implements PrefixesMaping {
+ StartElement startElement = null;
+
+ public PrefixMapingFromXml(final StartElement startElement) {
+ this.startElement = startElement;
+ }
+
+ @Override
+ public String getNamespace(final String prefix) {
+ return startElement.getNamespaceContext().getNamespaceURI(prefix);
+ }
+ }
+
+ public static class PrefixMapingFromJson implements PrefixesMaping {
+
+ @Override
+ public String getNamespace(final String prefix) {
+ return prefix;
+ }
+ }
+
+}
--- /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.codec.gson.helpers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+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.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.SchemaContext;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class SchemaContextUtils {
+ private final SchemaContext schemaContext;
+
+ private SchemaContextUtils(final SchemaContext schemaContext) {
+ this.schemaContext = Preconditions.checkNotNull(schemaContext);
+ }
+
+ public static SchemaContextUtils create(final SchemaContext schemaContext) {
+ return new SchemaContextUtils(schemaContext);
+ }
+
+ public URI findNamespaceByModuleName(final String moduleName) {
+ final Module module = this.findModuleByName(moduleName);
+ return module == null ? null : module.getNamespace();
+ }
+
+
+ public Module findModuleByName(final String moduleName) {
+ checkPreconditions();
+ Preconditions.checkArgument(moduleName != null && !moduleName.isEmpty());
+ return schemaContext.findModuleByName(moduleName, null);
+ }
+
+ public Module findModuleByNamespace(final URI namespace) {
+ this.checkPreconditions();
+ Preconditions.checkArgument(namespace != null);
+ return schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+ }
+
+ private void checkPreconditions() {
+ if (schemaContext == null) {
+ throw new IllegalStateException("Schema context isn't set.");
+ }
+ }
+
+ public DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name,
+ final URI namespace) {
+ Preconditions.<URI> checkNotNull(namespace);
+
+ final List<DataSchemaNode> potentialSchemaNodes = findInstanceDataChildrenByName(container, name);
+
+ Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+ @Override
+ public boolean apply(final DataSchemaNode node) {
+ return Objects.equal(node.getQName().getNamespace(), namespace);
+ }
+ };
+
+ Iterable<DataSchemaNode> result = Iterables.filter(potentialSchemaNodes, filter);
+ return Iterables.getFirst(result, null);
+ }
+
+ public List<DataSchemaNode> findInstanceDataChildrenByName(final DataNodeContainer container, final String name) {
+ Preconditions.<DataNodeContainer> checkNotNull(container);
+ Preconditions.<String> checkNotNull(name);
+
+ List<DataSchemaNode> instantiatedDataNodeContainers = new ArrayList<DataSchemaNode>();
+ collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name);
+ return instantiatedDataNodeContainers;
+ }
+
+ private void collectInstanceDataNodeContainers(final List<DataSchemaNode> potentialSchemaNodes,
+ final DataNodeContainer container, final String name) {
+
+ Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+ @Override
+ public boolean apply(final DataSchemaNode node) {
+ return Objects.equal(node.getQName().getLocalName(), name);
+ }
+ };
+
+ Iterable<DataSchemaNode> nodes = Iterables.filter(container.getChildNodes(), filter);
+
+ // Can't combine this loop with the filter above because the filter is
+ // lazily-applied by Iterables.filter.
+ for (final DataSchemaNode potentialNode : nodes) {
+ if (isInstantiatedDataSchema(potentialNode)) {
+ potentialSchemaNodes.add(potentialNode);
+ }
+ }
+
+ Iterable<ChoiceNode> choiceNodes = Iterables.filter(container.getChildNodes(), ChoiceNode.class);
+ Iterable<Set<ChoiceCaseNode>> map = Iterables.transform(choiceNodes, CHOICE_FUNCTION);
+
+ final Iterable<ChoiceCaseNode> allCases = Iterables.<ChoiceCaseNode> concat(map);
+ for (final ChoiceCaseNode caze : allCases) {
+ collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name);
+ }
+ }
+
+ public boolean isInstantiatedDataSchema(final DataSchemaNode node) {
+ return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode
+ || node instanceof ContainerSchemaNode || node instanceof ListSchemaNode
+ || node instanceof AnyXmlSchemaNode;
+ }
+
+ private final Function<ChoiceNode, Set<ChoiceCaseNode>> CHOICE_FUNCTION = new Function<ChoiceNode, Set<ChoiceCaseNode>>() {
+ @Override
+ public Set<ChoiceCaseNode> apply(final ChoiceNode node) {
+ return node.getCases();
+ }
+ };
+
+}
--- /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.codec.gson;
+
+import com.google.gson.stream.JsonReader;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.LoggingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StreamToNormalizedNodeTest {
+ private static final Logger LOG = LoggerFactory.getLogger(StreamToNormalizedNodeTest.class);
+ private static SchemaContext schemaContext;
+ private static String streamAsString;
+
+ @BeforeClass
+ public static void initialization() throws IOException, URISyntaxException {
+ schemaContext = loadModules("/complexjson/yang");
+ streamAsString = loadTextFile(new File(StreamToNormalizedNodeTest.class.getResource(
+ "/complexjson/complex-json.json").toURI()));
+ }
+
+ /**
+ * Demonstrates how to log events produced by a {@link JsonReader}.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void ownStreamWriterImplementationDemonstration() throws IOException {
+ // GSON's JsonReader reading from the loaded string (our event source)
+ final JsonReader reader = new JsonReader(new StringReader(streamAsString));
+
+ // StreamWriter which outputs SLF4J events
+ final LoggingNormalizedNodeStreamWriter logWriter = new LoggingNormalizedNodeStreamWriter();
+
+ // JSON -> StreamWriter parser
+ try (final JsonParserStream jsonHandler = JsonParserStream.create(logWriter, schemaContext)) {
+ // Process multiple readers, flush()/close() as needed
+ jsonHandler.parse(reader);
+ }
+ }
+
+ /**
+ * Demonstrates how to create an immutable NormalizedNode tree from a {@link JsonReader} and
+ * then writes the data back into string representation.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void immutableNormalizedNodeStreamWriterDemonstration() throws IOException {
+ /*
+ * This is the parsing part
+ */
+ // This is where we will output the nodes
+ final NormalizedNodeContainerBuilder<NodeIdentifier, ?, ?, ? extends NormalizedNode<?, ?>> parent =
+ Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(QName.create("dummy", "2014-12-31", "dummy")));
+
+ // StreamWriter which attaches NormalizedNode under parent
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(parent);
+
+ // JSON -> StreamWriter parser
+ try (JsonParserStream handler = JsonParserStream.create(streamWriter, schemaContext)) {
+ handler.parse(new JsonReader(new StringReader(streamAsString)));
+ }
+
+ // Finally build the node
+ final NormalizedNode<?, ?> parsedData = parent.build();
+ LOG.debug("Parsed NormalizedNodes: {}", parsedData);
+
+ /*
+ * This is the serialization part.
+ */
+ // We want to write the first child out
+ final DataContainerChild<? extends PathArgument, ?> firstChild = ((ContainerNode) parsedData).getValue().iterator().next();
+ LOG.debug("Serializing first child: {}", firstChild);
+
+ // String holder
+ final StringWriter writer = new StringWriter();
+
+ // StreamWriter which outputs JSON strings
+ final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.create(schemaContext, writer, 2);
+
+ // NormalizedNode -> StreamWriter
+ final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+
+ // Write multiple NormalizedNodes fluently, flush()/close() as needed
+ nodeWriter.write(firstChild).close();
+
+ // Just to put it somewhere
+ LOG.debug("Serialized JSON: {}", writer.toString());
+ }
+
+ private static SchemaContext loadModules(final String resourceDirectory) throws IOException, URISyntaxException {
+ YangContextParser parser = new YangParserImpl();
+ URI path = StreamToNormalizedNodeTest.class.getResource(resourceDirectory).toURI();
+ final File testDir = new File(path);
+ final String[] fileList = testDir.list();
+ final List<File> testFiles = new ArrayList<File>();
+ if (fileList == null) {
+ throw new FileNotFoundException(resourceDirectory);
+ }
+ for (String fileName : fileList) {
+ if (new File(testDir, fileName).isDirectory() == false) {
+ testFiles.add(new File(testDir, fileName));
+ }
+ }
+ return parser.parseFiles(testFiles);
+ }
+
+ private static String loadTextFile(final File file) throws IOException {
+ FileReader fileReader = new FileReader(file);
+ BufferedReader bufReader = new BufferedReader(fileReader);
+
+ String line = null;
+ StringBuilder result = new StringBuilder();
+ while ((line = bufReader.readLine()) != null) {
+ result.append(line);
+ }
+ bufReader.close();
+ return result.toString();
+ }
+}
--- /dev/null
+{
+ "complexjson:cont1": {
+ "lf12-any":[
+ {
+ "anyxml-in-data":"foo"
+ }
+ ],
+
+ "lf13-any":{
+ "anyxml-in-data":"foo"
+ },
+
+ "lf14-any":"anyxml data",
+
+ "lflst11":["lflst11 value1","lflst11 value2"],
+
+ "lst11":[
+ {
+ "key111":"key111 value",
+ "lf112":"/complexjson:cont1/complexjson:lflst11",
+ "lf113":"lf113 value",
+ "lf111":"lf111 value"
+ }
+ ],
+ "lf11" : "453",
+ "lf12_1" : "lf12 value",
+ "lf13" : "lf13 value",
+ "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
+ "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
+ "lf15_11" : "one two",
+ "lf15_12" : "complexjson:lf11",
+ "lf15_21" : "lf15_21 value",
+ "lf17" : "lf17 value",
+
+ "lst12":[
+ {
+ "lf121":"lf121 value"
+ }
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+module complexjson-augmentation {
+ namespace "ns:complex:json:augmentation";
+ prefix cjaug;
+
+ import complexjson {
+ prefix cj;
+ }
+
+ revision "2014-08-14" {
+ }
+
+ augment "/cj:cont1/cj:choc11/cj:c11A" {
+ leaf lf15_11 {
+ type string;
+ }
+ leaf lf15_12 {
+ type string;
+ }
+
+ }
+
+ augment "/cj:cont1" {
+ leaf lf12_1aug {
+ type string;
+ }
+ leaf lf12_2aug {
+ type string;
+ }
+ }
+
+ augment "/cj:cont1/cj:choc11/cj:c11A" {
+ leaf lf15_21aug {
+ type string;
+ }
+ }
+
+}
--- /dev/null
+module complexjson {
+ namespace "ns:complex:json";
+ prefix cj;
+
+ revision "2014-08-11" {
+ }
+
+
+ identity ident;
+
+ container cont1 {
+
+ anyxml lf12-any;
+ anyxml lf13-any;
+ anyxml lf14-any;
+
+ leaf lf11 {
+ type int32;
+ }
+
+ leaf-list lflst11 {
+ type string;
+ }
+
+ list lst11 {
+ key "key111 lf111";
+ leaf key111 {
+ type string;
+ }
+ leaf lf111 {
+ type string;
+ }
+ leaf lf112 {
+ type instance-identifier;
+ }
+ leaf lf113 {
+ type string;
+ }
+ }
+
+ list lst12 {
+ leaf lf121 {
+ type string;
+ }
+ leaf lf122 {
+ type string;
+ }
+ }
+
+
+ choice choc11 {
+ case c11A {
+ leaf lf13 {
+ type string;
+ }
+ }
+ leaf lf16 {
+ type string;
+ }
+ }
+
+ choice choc12 {
+ case c12A {
+ }
+ }
+ }
+
+
+ augment "/cont1/choc12" {
+ case c12B {
+ leaf lf17 {
+ type string;
+ }
+ }
+ }
+
+
+ augment "/cont1" {
+ container cont11 {
+ leaf lf111 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1" {
+ leaf lf12_1 {
+ type string;
+ }
+ leaf lf12_2 {
+ type string;
+ }
+ }
+
+ augment "/cont1" {
+ leaf lf12_3 {
+ type string;
+ }
+ }
+
+
+ augment "/cont1/choc11" {
+ case c11B {
+ leaf lf14_1 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1/choc11" {
+ case c11C {
+ leaf lf14_2 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1/choc11/c11A" {
+ leaf lf15_11 {
+ type bits {
+ bit one;
+ bit two;
+ bit three;
+ }
+ }
+ leaf lf15_12 {
+ type identityref {
+ base ident;
+ }
+ }
+
+ }
+
+ augment "/cont1/choc11/c11A" {
+ leaf lf15_21 {
+ type string;
+ }
+ }
+
+}
*/
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.collect.BiMap;
+import com.google.common.collect.HashBiMap;
import java.net.URI;
-import java.util.HashMap;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.ThreadLocalRandom;
-
import org.opendaylight.yangtools.yang.common.QName;
final class RandomPrefix {
- final Map<URI, String> prefixes = new HashMap<>();
- Iterable<Entry<URI, String>> getPrefixes() {
+ public static final char STARTING_CHAR = 'a';
+ public static final int CHARACTER_RANGE = 26;
+ public static final int PREFIX_MAX_LENGTH = 4;
+
+ public static final int MAX_COUNTER_VALUE = (int) Math.pow(CHARACTER_RANGE, PREFIX_MAX_LENGTH);
+ private static final int STARTING_WITH_XML = decode("xml");
+
+ private int counter = 0;
+
+ // BiMap to make values lookup faster
+ private final BiMap<URI, String> prefixes = HashBiMap.create();
+
+ Iterable<Map.Entry<URI, String>> getPrefixes() {
return prefixes.entrySet();
}
String encodePrefix(final QName qname) {
String prefix = prefixes.get(qname.getNamespace());
- if (prefix == null) {
- prefix = qname.getPrefix();
- if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
- final ThreadLocalRandom random = ThreadLocalRandom.current();
- do {
- final StringBuilder sb = new StringBuilder();
- for (int i = 0; i < 4; i++) {
- sb.append((char)('a' + random.nextInt(25)));
- }
-
- prefix = sb.toString();
- } while (prefixes.containsValue(prefix));
- }
-
- prefixes.put(qname.getNamespace(), prefix);
+ 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));
}
+
+ prefixes.put(qname.getNamespace(), prefix);
return prefix;
}
+
+ private boolean alreadyUsedPrefix(final String prefix) {
+ return prefixes.values().contains(prefix);
+ }
+
+ @VisibleForTesting
+ static int decode(final String s) {
+ int num = 0;
+ for (final char ch : s.toCharArray()) {
+ num *= CHARACTER_RANGE;
+ num += (ch - STARTING_CHAR);
+ }
+ return num;
+ }
+
+ @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;
+ }
+
+ return sb.reverse().toString();
+ }
}
package org.opendaylight.yangtools.yang.data.impl.schema;
import com.google.common.base.Preconditions;
+
+import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
+
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
*/
public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
-
-
@SuppressWarnings("rawtypes")
- private final Deque<NormalizedNodeContainerBuilder> builders;
-
+ private final Deque<NormalizedNodeContainerBuilder> builders = new ArrayDeque<>();
@SuppressWarnings("rawtypes")
private ImmutableNormalizedNodeStreamWriter( final NormalizedNodeContainerBuilder topLevelBuilder) {
- builders = new ArrayDeque<>();
builders.push(topLevelBuilder);
}
@Override
public void startMapEntryNode(final NodeIdentifierWithPredicates identifier,final int childSizeHint) throws IllegalArgumentException {
if(!(getCurrent() instanceof NormalizedNodeResultBuilder)) {
- Preconditions.checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder);
+ Preconditions.checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder || getCurrent() instanceof ImmutableOrderedMapNodeBuilder);
}
enter(Builders.mapEntryBuilder().withNodeIdentifier(identifier));
}
@Override
public void startOrderedMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
- if(!(getCurrent() instanceof NormalizedNodeResultBuilder)) {
- Preconditions.checkArgument(getCurrent() instanceof ImmutableOrderedMapNodeBuilder);
- }
- enter(Builders.mapBuilder().withNodeIdentifier(name));
+ checkDataNodeContainer();
+ enter(Builders.orderedMapBuilder().withNodeIdentifier(name));
}
@Override
}
+ @Override
+ public void flush() {
+ // no-op
+ }
+
+ @Override
+ public void close() throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
}
rwLock.writeLock().lock();
try {
final Optional<TreeNode> newRoot = m.getStrategy().apply(m.getRootModification(),
- Optional.<TreeNode>of(rootNode), rootNode.getSubtreeVersion().next());
+ Optional.<TreeNode>of(rootNode), m.getVersion());
Preconditions.checkState(newRoot.isPresent(), "Apply strategy failed to produce root node");
return new InMemoryDataTreeCandidate(PUBLIC_ROOT_PATH, root, rootNode, newRoot.get());
} finally {
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final RootModificationApplyOperation strategyTree;
private final InMemoryDataTreeSnapshot snapshot;
private final ModifiedNode rootNode;
+ private final Version version;
@GuardedBy("this")
private boolean sealed = false;
this.snapshot = Preconditions.checkNotNull(snapshot);
this.strategyTree = Preconditions.checkNotNull(resolver).snapshot();
this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode());
+ /*
+ * We could allocate version beforehand, since Version contract
+ * states two allocated version must be allways different.
+ *
+ * Preallocating version simplifies scenarios such as
+ * chaining of modifications, since version for particular
+ * node in modification and in data tree (if successfully
+ * commited) will be same and will not change.
+ *
+ */
+ this.version = snapshot.getRootNode().getSubtreeVersion().next();
}
ModifiedNode getRootModification() {
try {
return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
- snapshot.getRootNode().getSubtreeVersion().next());
+ version);
} catch (Exception e) {
LOG.error("Could not create snapshot for {}:{}", path,modification,e);
throw e;
}
/*
- * FIXME: Add advanced transaction chaining for modification of not rebased
- * modification.
- *
- * Current computation of tempRoot may yeld incorrect subtree versions
- * if there are multiple concurrent transactions, which may break
- * versioning preconditions for modification of previously occured write,
- * directly nested under parent node, since node version is derived from
- * subtree version.
- *
- * For deeper nodes subtree version is derived from their respective metadata
- * nodes, so this incorrect root subtree version is not affecting us.
+ * We will use preallocated version, this means returned snapshot will
+ * have same version each time this method is called.
*/
TreeNode originalSnapshotRoot = snapshot.getRootNode();
- Optional<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), originalSnapshotRoot.getSubtreeVersion().next());
+ Optional<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), version);
InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree);
return tempTree.newModification();
}
+
+ Version getVersion() {
+ return version;
+ }
}
*/
package org.opendaylight.yangtools.yang.data.impl.codec.xml;
-import org.junit.Assert;
-import org.junit.Before;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+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.data.impl.codec.xml.RandomPrefix;
+import org.opendaylight.yangtools.yang.common.QNameModule;
-/**
- * @author tkubas
- */
public class RandomPrefixTest {
- private RandomPrefix randomPrefix;
-
- /**
- * setup {@link #randomPrefix} instance
- */
- @Before
- public void setUp() {
- randomPrefix = new RandomPrefix();
+
+ @Test
+ public void testEncodeDecode() throws Exception {
+ final List<String> allGenerated = Lists.newArrayList();
+ for (int i = 0; i < RandomPrefix.MAX_COUNTER_VALUE; 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("a", allGenerated.get(0));
+ assertEquals("xml", allGenerated.get(RandomPrefix.decode("xml")));
+ assertEquals(allGenerated.size(), Sets.newHashSet(allGenerated).size());
+ }
+
+ @Test
+ public void testQNameWithPrefix() throws Exception {
+ final RandomPrefix a = new RandomPrefix();
+
+ final List<String> allGenerated = Lists.newArrayList();
+ for (int i = 0; i < RandomPrefix.MAX_COUNTER_VALUE; 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));
+ }
+
+ assertEquals(RandomPrefix.MAX_COUNTER_VALUE, 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()));
+ 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"));
}
- /**
- * Test method for {@link org.opendaylight.yangtools.yang.data.impl.codec.xml.RandomPrefix#encodeQName(QName)}.
- */
+
@Test
- public void testEncodeQName() {
- QName node = QName.create("","2013-06-07","node");
- String encodedQName = randomPrefix.encodeQName(node);
- Assert.assertNotNull(encodedQName);
- Assert.assertTrue("prefix is expected to contain 4 small letters as prefix but result is: "+encodedQName,
- encodedQName.matches("[a-z]{4}:node"));
+ public void test2QNames1Namespace() throws Exception {
+ final RandomPrefix a = new RandomPrefix();
+
+ final URI uri = URI.create("localhost");
+ 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));
}
+ @Test
+ public void testQNameNoPrefix() throws Exception {
+ final RandomPrefix a = new RandomPrefix();
+
+ final URI uri = URI.create("localhost");
+ QName qName = QName.create(uri, new Date(), "local-name");
+ assertEquals("a", a.encodePrefix(qName));
+ qName = QName.create(QNameModule.create(uri, new Date()), "", "local-name");
+ assertEquals("a", a.encodePrefix(qName));
+ qName = QName.create(QNameModule.create(URI.create("second"), new Date()), "", "local-name");
+ assertEquals("b", a.encodePrefix(qName));
+
+ }
}
import java.util.List;
import java.util.Set;
-
import javax.annotation.concurrent.Immutable;
/**
*/
Set<ModuleImport> getImports();
+ Set<Module> getSubmodules();
+
/**
* Returns <code>FeatureDefinition</code> instances which contain data from
* <b>feature</b> statements defined in the module.
*
* @param sourceIdentifier Source identifier
* @return schema source registration, which the subclass needs to
- * {@link SchemaSourceRegistration#close() once it expunges the source
+ * {@link SchemaSourceRegistration#close()} once it expunges the source
* from the cache.
*/
protected final SchemaSourceRegistration<T> register(final SourceIdentifier sourceIdentifier) {
*/
package org.opendaylight.yangtools.yang.model.repo.util;
+import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+@Beta
public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T> {
private static final class CacheEntry<T extends SchemaSourceRepresentation> {
private final SchemaSourceRegistration<T> reg;
private final Cache<SourceIdentifier, CacheEntry<T>> cache;
- protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final int maxSize) {
+ protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final CacheBuilder<Object, Object> builder) {
super(consumer, representation, Costs.IMMEDIATE);
- cache = CacheBuilder.newBuilder().softValues().maximumSize(maxSize).removalListener(LISTENER).build();
+ cache = builder.removalListener(LISTENER).build();
+ }
+
+ public static <R extends SchemaSourceRepresentation> InMemorySchemaSourceCache<R> createSoftCache(final SchemaSourceRegistry consumer, final Class<R> representation) {
+ return new InMemorySchemaSourceCache<>(consumer, representation, CacheBuilder.newBuilder().softValues());
}
@Override
*
* <ul>
* <li>{@link LengthConstraint#getErrorAppTag()} returns <code>length-out-of-specified-bounds</code>
- * <li>{@link LengthConstraint#getErrorMessage() returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
+ * <li>{@link LengthConstraint#getErrorMessage()} returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
* </ul>
*
* @see LengthConstraint
*
* <ul>
* <li>{@link RangeConstraint#getErrorAppTag()} returns <code>range-out-of-specified-bounds</code>
- * <li>{@link RangeConstraint#getErrorMessage() returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
+ * <li>{@link RangeConstraint#getErrorMessage()} returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
* </ul>
*
*
* @param pattern Regular expression, MUST NOT BE null.
* @param description Description associated with constraint.
* @param reference Reference associated with constraint.
- * @returnInstance of {@link PatternConstraint}
+ * @return Instance of {@link PatternConstraint}
*/
public static PatternConstraint newPatternConstraint(final String pattern, final Optional<String> description,
final Optional<String> reference) {
*
* <ul>
* <li>{@link LengthConstraint#getErrorAppTag()} returns <code>length-out-of-specified-bounds</code>
- * <li>{@link LengthConstraint#getErrorMessage() returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
+ * <li>{@link LengthConstraint#getErrorMessage()} returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
* </ul>
*
* @see LengthConstraint
*
* <ul>
* <li>{@link RangeConstraint#getErrorAppTag()} returns <code>range-out-of-specified-bounds</code>
- * <li>{@link RangeConstraint#getErrorMessage() returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
+ * <li>{@link RangeConstraint#getErrorMessage()} returns <code>The argument is out of bounds <<i>min</i>, <i>max</i> ></code>
* </ul>
*
*
* still result of processing only single unit of schema source (e.g. file, input stream). E.g.:
* <ul>
* <li>{@link java.lang.String} - textual representation of source code
- * <li>{@link InputStream} - input stream containing source code
+ * <li>{@link java.io.InputStream} - input stream containing source code
* <li>{@link com.google.common.io.ByteSource} - source for input streams containing source code
* <li>Parsed AST - abstract syntax tree, which is result of a parser, but still it is not linked
* against other schemas.
*
*
*/
-package org.opendaylight.yangtools.yang.model.util.repo;
\ No newline at end of file
+package org.opendaylight.yangtools.yang.model.util.repo;
rpc_stmt : RPC_KEYWORD string (SEMICOLON | (LEFT_BRACE (identifier_stmt |if_feature_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | input_stmt | output_stmt )* RIGHT_BRACE));
when_stmt : WHEN_KEYWORD string (SEMICOLON | (LEFT_BRACE (identifier_stmt |description_stmt | reference_stmt )* RIGHT_BRACE));
-augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)+ RIGHT_BRACE;
-uses_augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)+ RIGHT_BRACE;
+augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)* RIGHT_BRACE;
+uses_augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)* RIGHT_BRACE;
refine_anyxml_stmts : (identifier_stmt |must_stmt | config_stmt | mandatory_stmt | description_stmt | reference_stmt )*;
refine_case_stmts : (identifier_stmt |description_stmt | reference_stmt )*;
refine_choice_stmts : (identifier_stmt |default_stmt | config_stmt | mandatory_stmt | description_stmt | reference_stmt )*;
position_stmt : POSITION_KEYWORD string stmtend;
bit_stmt : BIT_KEYWORD string (SEMICOLON | (LEFT_BRACE (identifier_stmt |position_stmt | status_stmt | description_stmt | reference_stmt )* RIGHT_BRACE));
bits_specification : bit_stmt (bit_stmt | identifier_stmt)*;
-union_specification : type_stmt (identifier_stmt | type_stmt )+;
+union_specification : type_stmt (identifier_stmt | type_stmt )*;
identityref_specification : base_stmt ;
instance_identifier_specification : (require_instance_stmt )?;
require_instance_arg :string; // TRUE_KEYWORD | FALSE_KEYWORD;
* Returns when condition
*
* If when condition is present node defined by the parent data definition
- * statement is only valid when the returned XPath
- * expression conceptually evaluates to "true"
- * for a particular instance, then the node defined by the parent data
- * definition statement is valid; otherwise, it is not.
- *
+ * statement is only valid when the returned XPath expression conceptually
+ * evaluates to "true" for a particular instance, then the node defined by
+ * the parent data definition statement is valid; otherwise, it is not.
*
- * @return
+ * @return when condition as string
*/
String getWhenCondition();
*
* This constraint has meaning only if associated node is list or leaf-list.
*
- * @param minElements
+ * @param maxElements
* number of maximum required elements.
*/
void setMaxElements(Integer maxElements);
/**
* Build constraint definition
*
- * @return
+ * @return instance of ConstraintDefinition created from this builder
*/
ConstraintDefinition toInstance();
*
* This constraint has meaning only if associated node is list or leaf-list.
*
- * @param minElements number of maximum required elements.
+ * @param maxElements number of maximum required elements.
*/
void setMaxElements(Integer maxElements);
List<PatternConstraint> getPatterns();
/**
- * Set length restrictions for resulting type definition.
+ * Set pattern restrictions for resulting type definition.
*
- * @param lengths
- * Length restrictions of resulting type definition.
+ * @param patterns
+ * patterns restrictions of resulting type definition.
*/
void setPatterns(List<PatternConstraint> patterns);
Integer getFractionDigits();
/**
- *
- * Sets fractions digits of resulting type if it is derived
- * from <code>decimal</code> built-in type.
- *
- * @return fractions digits of resulting type
+ * Sets fractions digits of resulting type if it is derived from
+ * <code>decimal</code> built-in type.
*/
void setFractionDigits(Integer fractionDigits);
/**
* Sets extension builder, which declares this unknown node
- * @param extensionBuilder extension definition, which declares this unknown node
+ *
+ * @param extension
+ * extension builder, which declares this unknown node
*/
void setExtensionBuilder(ExtensionBuilder extension);
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.TreeMap;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.io.IOUtils;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Namespace_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
public final class BuilderUtils {
- private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private static final Logger LOG = LoggerFactory.getLogger(BuilderUtils.class);
- private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
private static final Splitter COLON_SPLITTER = Splitter.on(':');
private static final Date NULL_DATE = new Date(0L);
private static final String INPUT = "input";
* Class to be checked
* @param optional
* Original value
- * @return
+ * @return Optional object with type argument casted as cls
*/
private static <T> Optional<T> castOptional(final Class<T> cls, final Optional<?> optional) {
if (optional.isPresent()) {
* Find augment target node and perform augmentation.
*
* @param augment
+ * augment builder to process
* @param firstNodeParent
* parent of first node in path
- * @param path
- * path to augment target
* @return true if augmentation process succeed, false otherwise
*/
public static boolean processAugmentation(final AugmentationSchemaBuilder augment,
public static Map<String, TreeMap<Date, URI>> createYangNamespaceContext(
final Collection<? extends ParseTree> modules, final Optional<SchemaContext> context) {
- Map<String, TreeMap<Date, URI>> map = new HashMap<>();
+ Map<String, TreeMap<Date, URI>> namespaceContext = new HashMap<>();
+ Set<Submodule_stmtContext> submodules = new HashSet<>();
+ // first read ParseTree collection and separate modules and submodules
for (ParseTree module : modules) {
for (int i = 0; i < module.getChildCount(); i++) {
ParseTree moduleTree = module.getChild(i);
- if (moduleTree instanceof Module_stmtContext) {
+ if (moduleTree instanceof Submodule_stmtContext) {
+ // put submodule context to separate collection
+ submodules.add((Submodule_stmtContext) moduleTree);
+ } else if (moduleTree instanceof Module_stmtContext) {
+ // get name, revision and namespace from module
Module_stmtContext moduleCtx = (Module_stmtContext) moduleTree;
final String moduleName = ParserListenerUtils.stringFromNode(moduleCtx);
Date rev = null;
}
}
}
- TreeMap<Date, URI> revToNs = map.get(moduleName);
+ // update namespaceContext
+ TreeMap<Date, URI> revToNs = namespaceContext.get(moduleName);
if (revToNs == null) {
revToNs = new TreeMap<>();
revToNs.put(rev, namespace);
- map.put(moduleName, revToNs);
+ namespaceContext.put(moduleName, revToNs);
}
revToNs.put(rev, namespace);
}
}
}
+ // after all ParseTree-s are parsed update namespaceContext with modules
+ // from SchemaContext
if (context.isPresent()) {
for (Module module : context.get().getModules()) {
- TreeMap<Date, URI> revToNs = map.get(module.getName());
+ TreeMap<Date, URI> revToNs = namespaceContext.get(module.getName());
if (revToNs == null) {
revToNs = new TreeMap<>();
revToNs.put(module.getRevision(), module.getNamespace());
- map.put(module.getName(), revToNs);
+ namespaceContext.put(module.getName(), revToNs);
}
revToNs.put(module.getRevision(), module.getNamespace());
}
}
- return map;
+ // when all modules are processed, traverse submodules and update
+ // namespaceContext with mapping for submodules
+ for (Submodule_stmtContext submodule : submodules) {
+ final String moduleName = ParserListenerUtils.stringFromNode(submodule);
+ for (int i = 0; i < submodule.getChildCount(); i++) {
+ ParseTree subHeaderCtx = submodule.getChild(i);
+ if (subHeaderCtx instanceof Submodule_header_stmtsContext) {
+ for (int j = 0; j < subHeaderCtx.getChildCount(); j++) {
+ ParseTree belongsCtx = subHeaderCtx.getChild(j);
+ if (belongsCtx instanceof Belongs_to_stmtContext) {
+ final String belongsTo = ParserListenerUtils.stringFromNode(belongsCtx);
+ TreeMap<Date, URI> ns = namespaceContext.get(belongsTo);
+ if (ns == null) {
+ throw new YangParseException(moduleName, submodule.getStart().getLine(), String.format(
+ "Unresolved belongs-to statement: %s", belongsTo));
+ }
+ // submodule get namespace and revision from module
+ TreeMap<Date, URI> subNs = new TreeMap<>();
+ subNs.put(ns.firstKey(), ns.firstEntry().getValue());
+ namespaceContext.put(moduleName, subNs);
+ }
+ }
+ }
+ }
+ }
+ return namespaceContext;
}
}
final Map<String, ModuleImport> imports = new HashMap<>();
final Map<String, ModuleBuilder> importedModules = new HashMap<>();
- private final Map<String, Date> includedModules = new HashMap<>();
+ final Set<ModuleBuilder> addedSubmodules = new HashSet<>();
+ final Set<Module> submodules = new HashSet<>();
+ final Map<String, Date> includedModules = new HashMap<>();
private final Set<AugmentationSchema> augments = new LinkedHashSet<>();
private final List<AugmentationSchemaBuilder> augmentBuilders = new ArrayList<>();
buildChildren();
+ // SUBMODULES
+ for (ModuleBuilder submodule : addedSubmodules) {
+ submodules.add(submodule.build());
+ }
+
// FEATURES
for (FeatureBuilder fb : addedFeatures) {
features.add(fb.build());
includedModules.put(name, revision);
}
+ public void addSubmodule(final ModuleBuilder submodule) {
+ addedSubmodules.add(submodule);
+ }
+
protected String getSource() {
return source;
}
private final String organization;
private final String contact;
private final Set<ModuleImport> imports;
+ private final Set<Module> submodules;
private final Set<FeatureDefinition> features;
private final Set<NotificationDefinition> notifications;
private final Set<AugmentationSchema> augmentations;
this.name = checkNotNull(name, "Missing name");
this.sourcePath = sourcePath; //TODO: can this be nullable?
this.imports = ImmutableSet.<ModuleImport> copyOf(builder.imports.values());
+ this.submodules = ImmutableSet.<Module> copyOf(builder.submodules);
this.prefix = builder.getPrefix();
this.qnameModule = QNameModule.create(builder.getNamespace(),
return imports;
}
+ @Override
+ public Set<Module> getSubmodules() {
+ return submodules;
+ }
+
@Override
public Set<FeatureDefinition> getFeatures() {
return features;
* type body context
* @param actualPath
* current path in schema
- * @param namespace
- * current namespace
- * @param revision
- * current revision
- * @param prefix
- * current prefix
+ * @param moduleQName
+ * current module qname
* @param parent
* parent builder
* @return TypeDefinition object based on parsed values.
}
private void addSubmoduleToModule(final ModuleBuilder submodule, final ModuleBuilder module) {
+ module.addSubmodule(submodule);
submodule.setParent(module);
module.getDirtyNodes().addAll(submodule.getDirtyNodes());
module.getImports().putAll(submodule.getImports());
* Create a new instance.
*
* FIXME: the resulting type needs to be extracted, such that we can reuse
- * the "BaseListener" aspect, which need not be exposed to the user.
- * Maybe factor out a base class into repo.spi?
+ * the "BaseListener" aspect, which need not be exposed to the user. Maybe
+ * factor out a base class into repo.spi?
*
* @param namespaceContext
* @param sourcePath
* @param walker
* @param tree
- * @return
+ * @return new instance of YangParserListenerImpl
*/
public static YangParserListenerImpl create(final Map<String, TreeMap<Date, URI>> namespaceContext,
final String sourcePath, final ParseTreeWalker walker, final ParseTree tree) {
final ParseTree treeNode = ctx.getChild(i);
if (treeNode instanceof Prefix_stmtContext) {
importPrefix = stringFromNode(treeNode);
- }
- if (treeNode instanceof Revision_date_stmtContext) {
+ } else if (treeNode instanceof Revision_date_stmtContext) {
String importRevisionStr = stringFromNode(treeNode);
try {
importRevision = SIMPLE_DATE_FORMAT.parse(importRevisionStr);
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.io.ByteSource;
+
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
+
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver}
+ * instead.
+ */
+@Deprecated
@ThreadSafe
public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider<InputStream> {
/**
* Register new yang schema when it appears.
*/
- public synchronized ObjectRegistration<URL> registerSource(URL source) {
+ public synchronized ObjectRegistration<URL> registerSource(final URL source) {
checkArgument(source != null, "Supplied source must not be null");
InputStream yangStream = getInputStream(source);
YangModelDependencyInfo modelInfo = YangModelDependencyInfo.fromInputStream(yangStream);
}
@Override
- public synchronized Optional<InputStream> getSchemaSource(SourceIdentifier key) {
+ public synchronized Optional<InputStream> getSchemaSource(final SourceIdentifier key) {
SourceContext ctx = availableSources.get(key);
if (ctx != null) {
InputStream stream = getInputStream(ctx.getInstance());
}
@Override
- public Optional<InputStream> getSchemaSource(String name, Optional<String> version) {
+ public Optional<InputStream> getSchemaSource(final String name, final Optional<String> version) {
return getSchemaSource(SourceIdentifier.create(name, version));
}
- private static InputStream getInputStream(URL source) {
+ private static InputStream getInputStream(final URL source) {
InputStream stream;
try {
stream = source.openStream();
final SourceIdentifier identifier;
final YangModelDependencyInfo dependencyInfo;
- public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) {
+ public SourceContext(final URL instance, final SourceIdentifier identifier, final YangModelDependencyInfo modelInfo) {
super(instance);
this.identifier = identifier;
this.dependencyInfo = modelInfo;
}
}
- private synchronized void removeSource(SourceContext sourceContext) {
+ private synchronized void removeSource(final SourceContext sourceContext) {
boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext);
if (removed) {
tryToUpdateSchemaContext();
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Beta
-public class URLSchemaContextResolver implements SchemaSourceProvider<YangTextSchemaSource> {
+public class URLSchemaContextResolver implements AutoCloseable, SchemaSourceProvider<YangTextSchemaSource> {
private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
- private final Cache<SourceIdentifier, YangTextSchemaSource> sources = CacheBuilder.newBuilder().build();
private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+ private final Multimap<SourceIdentifier, YangTextSchemaSource> texts = ArrayListMultimap.create();
private final AtomicReference<Optional<SchemaContext>> currentSchemaContext =
new AtomicReference<>(Optional.<SchemaContext>absent());
+ private final InMemorySchemaSourceCache<ASTSchemaSource> cache;
+ private final SchemaListenerRegistration transReg;
private final SchemaSourceRegistry registry;
private final SchemaRepository repository;
private volatile Object version = new Object();
private volatile Object contextVersion = version;
+
private URLSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
this.repository = Preconditions.checkNotNull(repository);
this.registry = Preconditions.checkNotNull(registry);
+
+ final TextToASTTransformer t = TextToASTTransformer.create(repository, registry);
+ transReg = registry.registerSchemaSourceListener(t);
+
+ cache = InMemorySchemaSourceCache.createSoftCache(registry, ASTSchemaSource.class);
}
public static URLSchemaContextResolver create(final String name) {
LOG.trace("Resolved URL {} to source {}", url, ast);
final SourceIdentifier resolvedId = ast.getIdentifier();
- final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
- PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0));
-
- requiredSources.add(resolvedId);
- LOG.trace("Added source {} to schema context requirements", resolvedId);
- version = new Object();
- return new AbstractURLRegistration(text) {
- @Override
- protected void removeRegistration() {
- requiredSources.remove(resolvedId);
- LOG.trace("Removed source {} from schema context requirements", resolvedId);
- version = new Object();
- reg.close();
- sources.invalidate(resolvedId);
- }
- };
+ synchronized (this) {
+ texts.put(resolvedId, text);
+ LOG.debug("Populated {} with text", resolvedId);
+
+ final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+ PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, Costs.IMMEDIATE.getValue()));
+ requiredSources.add(resolvedId);
+ cache.schemaSourceEncountered(ast);
+ LOG.debug("Added source {} to schema context requirements", resolvedId);
+ version = new Object();
+
+ return new AbstractURLRegistration(text) {
+ @Override
+ protected void removeRegistration() {
+ synchronized (URLSchemaContextResolver.this) {
+ requiredSources.remove(resolvedId);
+ LOG.trace("Removed source {} from schema context requirements", resolvedId);
+ version = new Object();
+ reg.close();
+ texts.remove(resolvedId, text);
+ }
+ }
+ };
+ }
}
/**
Collection<SourceIdentifier> sources;
do {
v = version;
- sources = ImmutableList.copyOf(requiredSources);
+ sources = ImmutableSet.copyOf(requiredSources);
} while (v != version);
while (true) {
sc = Optional.of(f.checkedGet());
break;
} catch (SchemaResolutionException e) {
- LOG.info("Failed to fully assemble schema context for {}", sources, e);
+ LOG.debug("Failed to fully assemble schema context for {}", sources, e);
sources = e.getResolvedSources();
}
}
+ LOG.debug("Resolved schema context for {}", sources);
+
synchronized (this) {
if (contextVersion == cv) {
currentSchemaContext.set(sc);
}
@Override
- public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
- final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier);
- if (ret == null) {
+ public synchronized CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+ final Collection<YangTextSchemaSource> ret = texts.get(sourceIdentifier);
+
+ LOG.debug("Lookup {} result {}", sourceIdentifier, ret);
+ if (ret.isEmpty()) {
return Futures.<YangTextSchemaSource, SchemaSourceException>immediateFailedCheckedFuture(
new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered", sourceIdentifier));
}
- return Futures.immediateCheckedFuture(ret);
+ return Futures.immediateCheckedFuture(ret.iterator().next());
+ }
+
+ @Override
+ public void close() {
+ transReg.close();
}
}
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
-
import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
*/
private static void processDependencies(final Map<String, Map<Date, ModuleNodeImpl>> moduleGraph,
final Iterable<ModuleOrModuleBuilder> mmbs) {
- Map<URI, Object> allNS = new HashMap<>();
+ Map<URI, ModuleOrModuleBuilder> allNS = new HashMap<>();
// Create edges in graph
for (ModuleOrModuleBuilder mmb : mmbs) {
// check for existence of module with same namespace
if (allNS.containsKey(ns)) {
- Object mod = allNS.get(ns);
+ ModuleOrModuleBuilder mod = allNS.get(ns);
String name = null;
Date revision = null;
-
- if(mod instanceof ModuleOrModuleBuilder) {
- ModuleOrModuleBuilder modOrmodBuilder = ((ModuleOrModuleBuilder) mod);
- if(modOrmodBuilder.isModule()) {
- mod = ((ModuleOrModuleBuilder) mod).getModule();
- } else if (modOrmodBuilder.isModuleBuilder()) {
- mod = ((ModuleOrModuleBuilder) mod).getModuleBuilder();
- } else {
- LOGGER.warn("ModuleOrModuleBuilder is neither Module or ModuleBuilder");
- }
- }
- if (mod instanceof Module) {
- name = ((Module) mod).getName();
- revision = ((Module) mod).getRevision();
- } else if (mod instanceof ModuleBuilder) {
- name = ((ModuleBuilder) mod).getName();
- revision = ((ModuleBuilder) mod).getRevision();
- } else {
- LOGGER.warn("Module has no name: {}", mod);
+ if (mod.isModule()) {
+ name = mod.getModule().getName();
+ revision = mod.getModule().getRevision();
+ } else if (mod.isModuleBuilder()) {
+ name = mod.getModuleBuilder().getName();
+ revision = mod.getModuleBuilder().getRevision();
}
if (!(fromName.equals(name))) {
LOGGER.warn(