<feature name='yangtools-all' version='${project.version}'>
<feature version='${project.version}'>yangtools-models</feature>
- <feature version='${project.version}'>yangtools-binding</feature>
+ <feature version='${project.version}'>yangtools-data-binding</feature>
<feature version='${project.version}'>yangtools-common</feature>
<feature version='${project.version}'>yangtools-concepts</feature>
<feature version='${project.version}'>yangtools-binding-generator</feature>
<bundle>mvn:org.opendaylight.yangtools.model/ietf-topology/${ietf.topology.version}</bundle>
</feature>
- <feature name='yangtools-binding' version='${project.version}'>
+ <feature name='yangtools-data-binding' version='${project.version}'>
<feature version='${project.version}'>yangtools-concepts</feature>
+ <feature version='${project.version}'>yangtools-binding</feature>
<bundle>mvn:org.opendaylight.yangtools.thirdparty/antlr4-runtime-osgi-nohead/${antlr4.version}</bundle>
<bundle>mvn:commons-io/commons-io/${commons.io.version}</bundle>
- <bundle>mvn:org.opendaylight.yangtools/yang-binding/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/yang-data-api/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/yang-data-impl/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/yang-data-json/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/yang-parser-api/${project.version}</bundle>
</feature>
+ <feature name='yangtools-binding' version='${project.version}'>
+ <feature version='${project.version}'>yangtools-concepts</feature>
+ <bundle>mvn:com.google.guava/guava/${guava.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools/yang-binding/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools/util/${project.version}</bundle>
+ </feature>
+
<feature name='yangtools-concepts' version='${project.version}'>
<bundle>mvn:org.opendaylight.yangtools/concepts/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/yang-common/${project.version}</bundle>
- <bundle>wrap:mvn:com.google.guava/guava/${guava.version}</bundle>
- <bundle>wrap:mvn:org.eclipse.xtend/org.eclipse.xtend.lib/${xtend.version}</bundle>
- <bundle>wrap:mvn:org.eclipse.xtext/org.eclipse.xtext.xbase.lib/${xtend.version}</bundle>
+ <bundle>mvn:com.google.guava/guava/${guava.version}</bundle>
</feature>
<feature name="yangtools-common" version='${project.version}'>
<bundle>mvn:org.opendaylight.yangtools/util/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/object-cache-noop/${project.version}</bundle>
</feature>
<feature name='yangtools-binding-generator' version='${project.version}'>
- <feature version='${project.version}'>yangtools-binding</feature>
+ <feature version='${project.version}'>yangtools-data-binding</feature>
<bundle>mvn:org.javassist/javassist/${javassist.version}</bundle>
<bundle>mvn:org.apache.commons/commons-lang3/${commons.lang3.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/binding-generator-api/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/binding-generator-util/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/binding-model-api/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/binding-type-provider/${project.version}</bundle>
-
+ <bundle>wrap:mvn:org.eclipse.xtend/org.eclipse.xtend.lib/${xtend.version}</bundle>
+ <bundle>wrap:mvn:org.eclipse.xtext/org.eclipse.xtext.xbase.lib/${xtend.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools/yang-model-api/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools/yang-model-util/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools/yang-parser-api/${project.version}</bundle>
</feature>
</features>
package org.opendaylight.yangtools.yang.common;
/**
- *
- * Representation of Error in YANG enabled system.
- *
- * Which may be send / received by YANG modeled / enabled systems.
+ * Representation of an error.
*
*/
public interface RpcError {
+ public enum ErrorSeverity {
+ ERROR,
+ WARNING
+ }
+
+ public enum ErrorType {
+ /**
+ * Indicates an error occurred during transport of data, eg over the network.
+ */
+ TRANSPORT,
+
+ /**
+ * Indicates an error occurred during a remote procedure call.
+ */
+ RPC,
+
+ /**
+ * Indicates an error at a protocol layer, eg if invalid data was passed by the caller.
+ */
+ PROTOCOL,
+
+ /**
+ * Indicates an error occurred during internal processing.
+ */
+ APPLICATION
+ }
+
/**
+ * Returns the error severity, as determined by the application reporting the error.
*
- * Returns error severity, as determined by component reporting the error.
- *
- * @return error severity
+ * @return an {@link ErrorSeverity} enum.
*/
ErrorSeverity getSeverity();
/**
- *
- * Returns a string identifying the error condition.
- *
- * @return string identifying the error condition.
+ * Returns a short string that identifies the general type of error condition.
+ * <p>
+ * The following outlines suggested values as defined by netconf (<a href="https://tools.ietf.org/html/rfc6241#page-89">RFC 6241</a>):
+ * <pre>
+ * access_denied
+ * bad_attribute
+ * bad_element
+ * data_exists
+ * data_missing
+ * in_use
+ * invalid_value
+ * lock_denied
+ * malformed_message
+ * missing_attribute
+ * missing_element
+ * operation_failed
+ * operation_not_supported
+ * resource_denied
+ * rollback_failed
+ * too_big
+ * unknown_attribute
+ * unknown_element
+ * unknown_namespace
+ * </pre>
+ * @return a string if available or null otherwise.
*/
String getTag();
/**
+ * Returns a short string that identifies the specific type of error condition as
+ * determined by the application reporting the error.
*
- * Returns a string identifying the data-model-specific or
- * implementation-specific error condition, if one exists. This element will
- * not be present if no appropriate application error-tag can be associated
- * with a particular error condition. If a data-model-specific and an
- * implementation-specific error-app-tag both exist, then the
- * data-model-specific value MUST be used by the reporter.
- *
- * @return Returns a string identifying the data-model-specific or
- * implementation-specific error condition, or null if does not
- * exists.
+ * @return a string if available or null otherwise.
*/
String getApplicationTag();
/**
- *
* Returns a string suitable for human display that describes the error
- * condition. This element will not be present if no appropriate message is
- * provided for a particular error condition.
+ * condition.
*
- * @return returns an error description for human display.
+ * @return a message string.
*/
String getMessage();
/**
*
- * Contains protocol- or data-model-specific error content. This value may
- * be not be present if no such error content is provided for a particular
- * error condition.
- *
- * The list in Appendix A defines any mandatory error-info content for each
- * error. After any protocol-mandated content, a data model definition MAY
- * mandate that certain application-layer error information be included in
- * the error-info container.
- *
- * An implementation MAY include additional information to provide extended
- * and/or implementation- specific debugging information.
+ * Returns a string containing additional information to provide extended
+ * and/or implementation-specific debugging information.
*
- * @return
+ * @return a string if available or null otherwise.
*/
String getInfo();
/**
*
- * Return a cause if available.
+ * Returns an exception cause.
*
- * @return cause of this error, if error was triggered by exception.
+ * @return a Throwable if the error was triggered by exception, null otherwise.
*/
Throwable getCause();
/**
- * Returns the conceptual layer that on which the error occurred.
+ * Returns the conceptual layer at which the error occurred.
*
- * @return the conceptual layer that on which the error occurred.
+ * @return an {@link ErrorType} enum.
*/
ErrorType getErrorType();
-
- public enum ErrorSeverity {
- ERROR, WARNING,
- }
-
- public enum ErrorType {
- TRANSPORT, RPC, PROTOCOL, APPLICATION
- }
}
import java.util.Collection;
/**
+ * Represents a general result of a call, request, or operation.
*
- * Result of call to YANG enabled system.
- *
- *
- * @param <T> Return type
+ * @param <T> the result value type
*/
public interface RpcResult<T> {
/**
- * True if processing of request was successful
+ * Returns whether or not processing of the call was successful.
*
- * @return true if processing was successful.
+ * @return true if processing was successful, false otherwise.
*/
boolean isSuccessful();
/**
- *
- * Returns result of call or null if no result is available.
- *
- * @return result of call or null if no result is available.
- *
+ * Returns the value result of the call or null if no result is available.
*/
T getResult();
/**
+ * Returns a set of errors and warnings which occurred during processing
+ * the call.
*
- * Returns set of errors and warnings which occured during processing
- * the request.
- *
- * @return
+ * @return a Collection of {@link RpcError}
*/
Collection<RpcError> getErrors();
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications 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.common;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * A builder for creating RpcResult instances.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <T> the result value type
+ */
+public class RpcResultBuilder<T> {
+
+ private static class RpcResultImpl<T> implements RpcResult<T> {
+
+ private final Collection<RpcError> errors;
+ private final T result;
+ private final boolean successful;
+
+ RpcResultImpl( boolean successful, T result,
+ Collection<RpcError> errors ) {
+ this.successful = successful;
+ this.result = result;
+ this.errors = errors;
+ }
+
+ @Override
+ public Collection<RpcError> getErrors() {
+ return errors;
+ }
+
+ @Override
+ public T getResult() {
+ return result;
+ }
+
+ @Override
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ @Override
+ public String toString(){
+ return "RpcResult [successful=" + successful + ", result="
+ + result + ", errors=" + errors + "]";
+ }
+ }
+
+ private static class RpcErrorImpl implements RpcError {
+
+ private final String applicationTag;
+ private final String tag;
+ private final String info;
+ private final ErrorSeverity severity;
+ private final String message;
+ private final ErrorType errorType;
+ private final Throwable cause;
+
+ RpcErrorImpl( ErrorSeverity severity, ErrorType errorType,
+ String tag, String message, String applicationTag, String info,
+ Throwable cause ) {
+ this.severity = severity;
+ this.errorType = errorType;
+ this.tag = tag;
+ this.message = message;
+ this.applicationTag = applicationTag;
+ this.info = info;
+ this.cause = cause;
+ }
+
+ @Override
+ public String getApplicationTag() {
+ return applicationTag;
+ }
+
+ @Override
+ public String getTag() {
+ return tag;
+ }
+
+ @Override
+ public String getInfo() {
+ return info;
+ }
+
+ @Override
+ public ErrorSeverity getSeverity() {
+ return severity;
+ }
+
+ @Override
+ public String getMessage(){
+ return message;
+ }
+
+ @Override
+ public ErrorType getErrorType() {
+ return errorType;
+ }
+
+ @Override
+ public Throwable getCause() {
+ return cause;
+ }
+
+ @Override
+ public String toString(){
+ return "RpcError [message=" + message + ", severity="
+ + severity + ", errorType=" + errorType + ", tag=" + tag
+ + ", applicationTag=" + applicationTag + ", info=" + info
+ + ", cause=" + cause + "]";
+ }
+ }
+
+ private Collection<RpcError> errors;
+ private T result;
+ private final boolean successful;
+
+ private RpcResultBuilder( boolean successful, T result ) {
+ this.successful = successful;
+ this.result = result;
+ }
+
+ /**
+ * Returns a builder for a successful result.
+ */
+ public static <T> RpcResultBuilder<T> success() {
+ return new RpcResultBuilder<T>( true, null );
+ }
+
+ /**
+ * Returns a builder for a successful result.
+ *
+ * @param result the result value
+ */
+ public static <T> RpcResultBuilder<T> success( T result ) {
+ return new RpcResultBuilder<T>( true, result );
+ }
+
+ /**
+ * Returns a builder for a failed result.
+ */
+ public static <T> RpcResultBuilder<T> failed() {
+ return new RpcResultBuilder<T>( false, null );
+ }
+
+ /**
+ * Sets the value of the result.
+ *
+ * @param result the result value
+ */
+ public RpcResultBuilder<T> withResult( T result ) {
+ this.result = result;
+ return this;
+ }
+
+ private void addError( ErrorSeverity severity, ErrorType errorType,
+ String tag, String message, String applicationTag, String info,
+ Throwable cause ) {
+
+ if( errors == null ) {
+ errors = new ArrayList<>();
+ }
+
+ errors.add( new RpcErrorImpl( severity, errorType, tag, message,
+ applicationTag, info, cause ) );
+ }
+
+ /**
+ * Adds a warning to the result.
+ *
+ * @param errorType the conceptual layer at which the warning occurred.
+ * @param tag a short string that identifies the general type of warning condition. See
+ * {@link RpcError#getTag} for a list of suggested values.
+ * @param message a string suitable for human display that describes the warning condition.
+ */
+ public RpcResultBuilder<T> withWarning( ErrorType errorType, String tag, String message ) {
+ addError( ErrorSeverity.WARNING, errorType, tag, message, null, null, null );
+ return this;
+ }
+
+ /**
+ * Adds a warning to the result.
+ *
+ * @param errorType the conceptual layer at which the warning occurred.
+ * @param tag a short string that identifies the general type of warning condition. See
+ * {@link RpcError#getTag} for a list of suggested values.
+ * @param message a string suitable for human display that describes the warning condition.
+ * @param applicationTag a short string that identifies the specific type of warning condition.
+ * @param info a string containing additional information to provide extended
+ * and/or implementation-specific debugging information.
+ * @param cause the exception that triggered the warning.
+ */
+ public RpcResultBuilder<T> withWarning( ErrorType errorType, String tag, String message,
+ String applicationTag, String info, Throwable cause ) {
+ addError( ErrorSeverity.WARNING, errorType, tag, message, applicationTag, info, cause );
+ return this;
+ }
+
+ /**
+ * Adds an error to the result. The general error tag defaults to "operation-failed".
+ *
+ * @param errorType the conceptual layer at which the error occurred.
+ * @param message a string suitable for human display that describes the error condition.
+ */
+ public RpcResultBuilder<T> withError( ErrorType errorType, String message ) {
+ addError( ErrorSeverity.ERROR, errorType, "operation-failed", message, null, null, null );
+ return this;
+ }
+
+ /**
+ * Adds an error to the result.
+ *
+ * @param errorType the conceptual layer at which the error occurred.
+ * @param tag a short string that identifies the general type of error condition. See
+ * {@link RpcError#getTag} for a list of suggested values.
+ * @param message a string suitable for human display that describes the error condition.
+ */
+ public RpcResultBuilder<T> withError( ErrorType errorType, String tag, String message ) {
+ addError( ErrorSeverity.ERROR, errorType, tag, message, null, null, null );
+ return this;
+ }
+
+ /**
+ * Adds an error to the result.
+ *
+ * @param errorType the conceptual layer at which the error occurred.
+ * @param tag a short string that identifies the general type of error condition. See
+ * {@link RpcError#getTag} for a list of suggested values.
+ * @param message a string suitable for human display that describes the error condition.
+ * @param applicationTag a short string that identifies the specific type of error condition.
+ * @param info a string containing additional information to provide extended
+ * and/or implementation-specific debugging information.
+ * @param cause the exception that triggered the error.
+ */
+ public RpcResultBuilder<T> withError( ErrorType errorType, String tag, String message,
+ String applicationTag, String info, Throwable cause ) {
+ addError( ErrorSeverity.ERROR, errorType, tag, message, applicationTag, info, cause );
+ return this;
+ }
+
+ public RpcResult<T> build() {
+
+ return new RpcResultImpl<T>( successful, result,
+ errors != null ? errors : Collections.<RpcError>emptyList() );
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications 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.common;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * Unit tests for RpcResultBuilder.
+ *
+ * @author Thomas Pantelis
+ */
+public class RpcResultBuilderTest {
+
+ @Test
+ public void testSuccess() {
+ RpcResult<String> result = RpcResultBuilder.<String>success().withResult( "foo" ).build();
+ verifyRpcResult( result, true, "foo" );
+ assertNotNull( "getErrors returned null", result.getErrors() );
+ assertEquals( "getErrors size", 0, result.getErrors().size() );
+
+ result = RpcResultBuilder.<String>success( "bar" ).build();
+ verifyRpcResult( result, true, "bar" );
+ }
+
+ @Test
+ public void testFailed() {
+ Throwable cause = new Throwable( "mock cause" );
+ RpcResult<String> result = RpcResultBuilder.<String>failed()
+ .withError( ErrorType.PROTOCOL, "error message 1" )
+ .withError( ErrorType.APPLICATION, "lock_denied", "error message 2" )
+ .withError( ErrorType.RPC, "in-use", "error message 3", "my-app-tag", "my-info", cause )
+ .build();
+ verifyRpcResult( result, false, null );
+ verifyRpcError( result, 0, ErrorSeverity.ERROR, ErrorType.PROTOCOL, "operation-failed",
+ "error message 1", null, null, null );
+ verifyRpcError( result, 1, ErrorSeverity.ERROR, ErrorType.APPLICATION, "lock_denied",
+ "error message 2", null, null, null );
+ verifyRpcError( result, 2, ErrorSeverity.ERROR, ErrorType.RPC, "in-use",
+ "error message 3", "my-app-tag", "my-info", cause );
+ assertEquals( "getErrors size", 3, result.getErrors().size() );
+ }
+
+ @Test
+ public void testWithWarnings() {
+ Throwable cause = new Throwable( "mock cause" );
+ RpcResult<String> result = RpcResultBuilder.<String>success()
+ .withWarning( ErrorType.APPLICATION, "lock_denied", "message 1" )
+ .withWarning( ErrorType.RPC, "in-use", "message 2", "my-app-tag", "my-info", cause )
+ .build();
+ verifyRpcResult( result, true, null );
+ verifyRpcError( result, 0, ErrorSeverity.WARNING, ErrorType.APPLICATION, "lock_denied",
+ "message 1", null, null, null );
+ verifyRpcError( result, 1, ErrorSeverity.WARNING, ErrorType.RPC, "in-use",
+ "message 2", "my-app-tag", "my-info", cause );
+ assertEquals( "getErrors size", 2, result.getErrors().size() );
+ }
+
+ void verifyRpcError( RpcResult<?> result, int errorIndex, ErrorSeverity expSeverity,
+ ErrorType expErrorType, String expTag, String expMessage, String expAppTag,
+ String expInfo, Throwable expCause ) {
+
+ List<RpcError> errors = new ArrayList<>( result.getErrors() );
+ assertTrue( "Expected error at index " + errorIndex + " not found",
+ errorIndex < errors.size() );
+ RpcError error = errors.get( errorIndex );
+ assertEquals( "getSeverity", expSeverity, error.getSeverity() );
+ assertEquals( "getErrorType", expErrorType, error.getErrorType() );
+ assertEquals( "getTag", expTag, error.getTag() );
+ assertEquals( "getMessage", expMessage, error.getMessage() );
+ assertEquals( "getApplicationTag", expAppTag, error.getApplicationTag() );
+ assertEquals( "getInfo", expInfo, error.getInfo() );
+ assertEquals( "getCause", expCause, error.getCause() );
+ }
+
+ void verifyRpcResult( RpcResult<?> result, boolean expSuccess, Object expValue ) {
+ assertEquals( "isSuccessful", expSuccess, result.isSuccessful() );
+ assertEquals( "getResult", expValue, result.getResult() );
+ }
+}
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.ThreadLocalRandom;
+import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
return InstanceIdentifier.create(result);
}
- public static Element serialize(final InstanceIdentifier data, final Element element) {
- Preconditions.checkNotNull(data, "Variable should contain instance of instance identifier and can't be null");
+ public static Element serialize(final InstanceIdentifier id, final Element element) {
+ Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
Preconditions.checkNotNull(element, "DOM element can't be null");
- Map<String, String> prefixes = new HashMap<>();
- StringBuilder textContent = new StringBuilder();
- for (PathArgument pathArgument : data.getPathArguments()) {
- textContent.append('/');
- writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
- if (pathArgument instanceof NodeIdentifierWithPredicates) {
- Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
-
- for (QName keyValue : predicates.keySet()) {
- String predicateValue = String.valueOf(predicates.get(keyValue));
- textContent.append('[');
- writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
- textContent.append("='");
- textContent.append(predicateValue);
- textContent.append("']");
- }
- } else if (pathArgument instanceof NodeWithValue) {
- textContent.append("[.='");
- textContent.append(((NodeWithValue) pathArgument).getValue());
- textContent.append("']");
- }
+
+ final RandomPrefix prefixes = new RandomPrefix();
+ final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+ for (Entry<URI, String> e: prefixes.getPrefixes()) {
+ element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
}
- element.setTextContent(textContent.toString());
+ element.setTextContent(str);
return element;
}
return null;
}
}
-
- private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, final QName qName,
- final Map<String, String> prefixes) {
- String namespace = qName.getNamespace().toString();
- String prefix = prefixes.get(namespace);
- if (prefix == null) {
- prefix = qName.getPrefix();
- if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
- prefix = generateNewPrefix(prefixes.values());
- }
- }
-
- element.setAttribute("xmlns:" + prefix, namespace.toString());
- textContent.append(prefix);
- prefixes.put(namespace, prefix);
-
- textContent.append(':');
- textContent.append(qName.getLocalName());
- }
-
- private static String generateNewPrefix(final Collection<String> prefixes) {
- String result;
-
- final ThreadLocalRandom random = ThreadLocalRandom.current();
- do {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < 4; i++) {
- sb.append('a' + random.nextInt(25));
- }
-
- result = sb.toString();
- } while (prefixes.contains(result));
-
- return result;
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec.xml;
+
+import java.net.URI;
+import java.util.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() {
+ return prefixes.entrySet();
+ }
+
+ String encodeQName(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('a' + random.nextInt(25));
+ }
+
+ prefix = sb.toString();
+ } while (prefixes.containsValue(prefix));
+ }
+
+ prefixes.put(qname.getNamespace(), prefix);
+ }
+
+ return prefix + ':' + qname.getLocalName();
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.yangtools.yang.data.impl.codec.xml;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+import java.util.Map.Entry;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+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.SchemaNode;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class XmlStreamUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
+
+ public static void writeDataDocument(final XMLStreamWriter writer, final CompositeNode data, final SchemaNode schema, final XmlCodecProvider codecProvider) throws XMLStreamException {
+ // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
+ // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
+
+ writer.writeStartDocument();
+ writeData(writer, data, schema, codecProvider);
+ writer.writeEndDocument();
+ writer.flush();
+ }
+
+ public static void writeDataDocument(final XMLStreamWriter writer, final CompositeNode data, final XmlCodecProvider codecProvider) throws XMLStreamException {
+ writeDataDocument(writer, data, null, codecProvider);
+ }
+
+ public static void write(final XMLStreamWriter writer, final InstanceIdentifier id) throws XMLStreamException {
+ Preconditions.checkNotNull(writer, "Writer may not be null");
+ Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+
+ final RandomPrefix prefixes = new RandomPrefix();
+ final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+ for (Entry<URI, String> e: prefixes.getPrefixes()) {
+ writer.writeNamespace(e.getValue(), e.getKey().toString());
+ }
+ writer.writeCharacters(str);
+ }
+
+ public static void writeData(final XMLStreamWriter writer, final Node<?> data, final SchemaNode schema, final XmlCodecProvider codecProvider) throws XMLStreamException {
+ final QName qname = data.getNodeType();
+ final String pfx = qname.getPrefix() != null ? qname.getPrefix() : "";
+ final String ns;
+ if (qname.getNamespace() != null) {
+ ns = qname.getNamespace().toString();
+ } else {
+ ns = "";
+ }
+
+ writer.writeStartElement(pfx, qname.getLocalName(), ns);
+ if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
+ for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
+ writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue());
+ }
+ }
+
+ if (data instanceof SimpleNode<?>) {
+ // Simple node
+ if (schema instanceof LeafListSchemaNode) {
+ writeValue(writer, ((LeafListSchemaNode) schema).getType(), codecProvider, data.getValue());
+ } else if (schema instanceof LeafSchemaNode) {
+ writeValue(writer, ((LeafSchemaNode) schema).getType(), codecProvider, data.getValue());
+ } else {
+ Object value = data.getValue();
+ if (value != null) {
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+ } else {
+ // CompositeNode
+ for (Node<?> child : ((CompositeNode) data).getValue()) {
+ DataSchemaNode childSchema = null;
+ if (schema instanceof DataNodeContainer) {
+ childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
+ if (LOG.isDebugEnabled()) {
+ if (childSchema == null) {
+ LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName());
+ }
+ }
+ }
+
+ writeData(writer, child, childSchema, codecProvider);
+ }
+ }
+
+ writer.writeEndElement();
+ }
+
+ public static void writeValue(final XMLStreamWriter writer, final TypeDefinition<?> type, final XmlCodecProvider codecProvider, final Object nodeValue) throws XMLStreamException {
+ TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+ if (baseType instanceof IdentityrefTypeDefinition) {
+ if (nodeValue instanceof QName) {
+ QName value = (QName) nodeValue;
+ String prefix = "x";
+ if (value.getPrefix() != null && !value.getPrefix().isEmpty()) {
+ prefix = value.getPrefix();
+ }
+
+ writer.writeNamespace(prefix, value.getNamespace().toString());
+ writer.writeCharacters(prefix + ':' + value.getLocalName());
+ } else {
+ Object value = nodeValue;
+ LOG.debug("Value of {}:{} is not instance of QName but is {}", baseType.getQName().getNamespace(),
+ baseType.getQName().getLocalName(), value != null ? value.getClass() : "null");
+ if (value != null) {
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+ } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+ if (nodeValue instanceof InstanceIdentifier) {
+ write(writer, (InstanceIdentifier)nodeValue);
+ } else {
+ Object value = nodeValue;
+ LOG.debug("Value of {}:{} is not instance of InstanceIdentifier but is {}", baseType.getQName()
+ .getNamespace(), //
+ baseType.getQName().getLocalName(), value != null ? value.getClass() : "null");
+ if (value != null) {
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+ } else {
+ if (nodeValue != null) {
+ final TypeDefinitionAwareCodec<Object, ?> codec = codecProvider.codecFor(baseType);
+ String text;
+ if (codec != null) {
+ try {
+ text = codec.serialize(nodeValue);
+ } catch (ClassCastException e) {
+ LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", nodeValue, baseType, e);
+ text = String.valueOf(nodeValue);
+ }
+ } else {
+ LOG.error("Failed to find codec for {}, falling back to using stream", baseType);
+ text = String.valueOf(nodeValue);
+ }
+ writer.writeCharacters(text);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec.xml;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Common XML-related utility methods, which are not specific to a particular
+ * JAXP API.
+ */
+final class XmlUtils {
+
+ private XmlUtils() {
+
+ }
+
+ public static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
+ TypeDefinition<?> superType = type;
+ while (superType.getBaseType() != null) {
+ superType = superType.getBaseType();
+ }
+ return superType;
+ }
+
+ static String encodeIdentifier(final RandomPrefix prefixes, final InstanceIdentifier id) {
+ StringBuilder textContent = new StringBuilder();
+ for (PathArgument pathArgument : id.getPathArguments()) {
+ textContent.append('/');
+ textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
+ if (pathArgument instanceof NodeIdentifierWithPredicates) {
+ Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+
+ for (QName keyValue : predicates.keySet()) {
+ String predicateValue = String.valueOf(predicates.get(keyValue));
+ textContent.append('[');
+ textContent.append(prefixes.encodeQName(keyValue));
+ textContent.append("='");
+ textContent.append(predicateValue);
+ textContent.append("']");
+ }
+ } else if (pathArgument instanceof NodeWithValue) {
+ textContent.append("[.='");
+ textContent.append(((NodeWithValue) pathArgument).getValue());
+ textContent.append("']");
+ }
+ }
+
+ return textContent.toString();
+ }
+}
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
-import org.opendaylight.yangtools.yang.model.api.*;
-import java.util.*;
+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.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
public final class SchemaUtils {
private SchemaUtils() {
}
- public static DataSchemaNode findSchemaForChild(DataNodeContainer schema, QName qname) {
+ public static final Optional<DataSchemaNode> findFirstSchema(final QName qname, final Set<DataSchemaNode> dataSchemaNode) {
+ if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) {
+ for (DataSchemaNode dsn : dataSchemaNode) {
+ if (qname.isEqualWithoutRevision(dsn.getQName())) {
+ return Optional.<DataSchemaNode> of(dsn);
+ } else if (dsn instanceof ChoiceNode) {
+ for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+ Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
+ if (foundDsn != null && foundDsn.isPresent()) {
+ return foundDsn;
+ }
+ }
+ }
+ }
+ }
+ return Optional.absent();
+ }
+
+ public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname) {
Set<DataSchemaNode> childNodes = schema.getChildNodes();
return findSchemaForChild(schema, qname, childNodes);
}
- public static DataSchemaNode findSchemaForChild(DataNodeContainer schema, QName qname, Set<DataSchemaNode> childNodes) {
- Optional<DataSchemaNode> childSchema = XmlDocumentUtils.findFirstSchema(qname, childNodes);
+ public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname, final Set<DataSchemaNode> childNodes) {
+ Optional<DataSchemaNode> childSchema = findFirstSchema(qname, childNodes);
Preconditions.checkState(childSchema.isPresent(),
"Unknown child(ren) node(s) detected, identified by: %s, in: %s", qname, schema);
return childSchema.get();
}
- public static AugmentationSchema findSchemaForAugment(AugmentationTarget schema, Set<QName> qNames) {
+ public static AugmentationSchema findSchemaForAugment(final AugmentationTarget schema, final Set<QName> qNames) {
Optional<AugmentationSchema> schemaForAugment = findAugment(schema, qNames);
Preconditions.checkState(schemaForAugment.isPresent(), "Unknown augmentation node detected, identified by: %s, in: %s",
qNames, schema);
return schemaForAugment.get();
}
- public static AugmentationSchema findSchemaForAugment(ChoiceNode schema, Set<QName> qNames) {
+ public static AugmentationSchema findSchemaForAugment(final ChoiceNode schema, final Set<QName> qNames) {
Optional<AugmentationSchema> schemaForAugment = Optional.absent();
for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
return schemaForAugment.get();
}
- private static Optional<AugmentationSchema> findAugment(AugmentationTarget schema, Set<QName> qNames) {
+ private static Optional<AugmentationSchema> findAugment(final AugmentationTarget schema, final Set<QName> qNames) {
for (AugmentationSchema augment : schema.getAvailableAugmentations()) {
HashSet<QName> qNamesFromAugment = Sets.newHashSet(Collections2.transform(augment.getChildNodes(), new Function<DataSchemaNode, QName>() {
@Override
- public QName apply(DataSchemaNode input) {
+ public QName apply(final DataSchemaNode input) {
return input.getQName();
}
}));
return Optional.absent();
}
- public static DataSchemaNode findSchemaForChild(ChoiceNode schema, QName childPartialQName) {
+ public static DataSchemaNode findSchemaForChild(final ChoiceNode schema, final QName childPartialQName) {
for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
- Optional<DataSchemaNode> childSchema = XmlDocumentUtils.findFirstSchema(childPartialQName,
- choiceCaseNode.getChildNodes());
+ Optional<DataSchemaNode> childSchema = findFirstSchema(childPartialQName, choiceCaseNode.getChildNodes());
if (childSchema.isPresent()) {
return childSchema.get();
}
*
* @return Map with all child nodes, to their most top augmentation
*/
- public static Map<QName, ChoiceNode> mapChildElementsFromChoices(DataNodeContainer schema) {
+ public static Map<QName, ChoiceNode> mapChildElementsFromChoices(final DataNodeContainer schema) {
Set<DataSchemaNode> childNodes = schema.getChildNodes();
return mapChildElementsFromChoices(schema, childNodes);
}
- private static Map<QName, ChoiceNode> mapChildElementsFromChoices(DataNodeContainer schema, Set<DataSchemaNode> childNodes) {
+ private static Map<QName, ChoiceNode> mapChildElementsFromChoices(final DataNodeContainer schema, final Set<DataSchemaNode> childNodes) {
Map<QName, ChoiceNode> mappedChoices = Maps.newLinkedHashMap();
for (final DataSchemaNode childSchema : childNodes) {
return mappedChoices;
}
- private static boolean isFromAugment(DataNodeContainer schema, DataSchemaNode childSchema) {
+ private static boolean isFromAugment(final DataNodeContainer schema, final DataSchemaNode childSchema) {
if(schema instanceof AugmentationTarget == false) {
return false;
}
*
* @return Map with all child nodes, to their most top augmentation
*/
- public static Map<QName, AugmentationSchema> mapChildElementsFromAugments(AugmentationTarget schema) {
+ public static Map<QName, AugmentationSchema> mapChildElementsFromAugments(final AugmentationTarget schema) {
Map<QName, AugmentationSchema> childNodesToAugmentation = Maps.newLinkedHashMap();
for (DataSchemaNode child : ((DataNodeContainer) schema).getChildNodes()) {
// If is not augmented child, continue
- if (augments.containsKey(child.getQName()) == false)
+ if (augments.containsKey(child.getQName()) == false) {
continue;
+ }
AugmentationSchema mostTopAugmentation = augments.get(child.getQName());
*
* In case of choice, augment and cases, step in.
*/
- public static Set<QName> getChildNodesRecursive(DataNodeContainer nodeContainer) {
+ public static Set<QName> getChildNodesRecursive(final DataNodeContainer nodeContainer) {
Set<QName> allChildNodes = Sets.newHashSet();
for (DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
* Schema of child node from augment is incomplete, therefore its useless for xml <-> normalizedNode translation.
*
*/
- public static Set<DataSchemaNode> getRealSchemasForAugment(AugmentationTarget targetSchema, AugmentationSchema augmentSchema) {
+ public static Set<DataSchemaNode> getRealSchemasForAugment(final AugmentationTarget targetSchema, final AugmentationSchema augmentSchema) {
if(targetSchema.getAvailableAugmentations().contains(augmentSchema) == false) {
return Collections.emptySet();
}
Set<DataSchemaNode> realChildNodes = Sets.newHashSet();
if(targetSchema instanceof DataNodeContainer) {
- realChildNodes = getRealSchemasForAugment((DataNodeContainer)targetSchema, augmentSchema);
+ realChildNodes = getRealSchemasForAugment((DataNodeContainer)targetSchema, augmentSchema);
} else if(targetSchema instanceof ChoiceNode) {
for (DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
for (ChoiceCaseNode choiceCaseNode : ((ChoiceNode) targetSchema).getCases()) {
return realChildNodes;
}
- public static Set<DataSchemaNode> getRealSchemasForAugment(DataNodeContainer targetSchema,
- AugmentationSchema augmentSchema) {
+ public static Set<DataSchemaNode> getRealSchemasForAugment(final DataNodeContainer targetSchema,
+ final AugmentationSchema augmentSchema) {
Set<DataSchemaNode> realChildNodes = Sets.newHashSet();
for (DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
DataSchemaNode realChild = targetSchema.getDataChildByName(dataSchemaNode.getQName());
return realChildNodes;
}
- public static Optional<ChoiceCaseNode> detectCase(ChoiceNode schema, DataContainerChild<?, ?> child) {
+ public static Optional<ChoiceCaseNode> detectCase(final ChoiceNode schema, final DataContainerChild<?, ?> child) {
for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
if (child instanceof AugmentationNode
&& belongsToCaseAugment(choiceCaseNode,
return Optional.absent();
}
- public static boolean belongsToCaseAugment(ChoiceCaseNode caseNode, InstanceIdentifier.AugmentationIdentifier childToProcess) {
+ public static boolean belongsToCaseAugment(final ChoiceCaseNode caseNode, final InstanceIdentifier.AugmentationIdentifier childToProcess) {
for (AugmentationSchema augmentationSchema : caseNode.getAvailableAugmentations()) {
Set<QName> currentAugmentChildNodes = Sets.newHashSet();
return false;
}
- public static InstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(AugmentationSchema schema) {
+ public static InstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(final AugmentationSchema schema) {
return new InstanceIdentifier.AugmentationIdentifier(getChildQNames(schema));
}
- public static Set<QName> getChildQNames(AugmentationSchema schema) {
+ public static Set<QName> getChildQNames(final AugmentationSchema schema) {
Set<QName> qnames = Sets.newHashSet();
for (DataSchemaNode dataSchemaNode : schema.getChildNodes()) {
import java.util.Date;
import java.util.List;
import java.util.Map;
-import java.util.Stack;
+import java.util.Deque;
+import java.util.LinkedList;
import org.junit.Assert;
import org.junit.Before;
}
private static void checkFamilyBinding(final CompositeNode treeRoot) throws Exception {
- Stack<CompositeNode> jobQueue = new Stack<>();
+ Deque<CompositeNode> jobQueue = new LinkedList<>();
jobQueue.push(treeRoot);
while (!jobQueue.isEmpty()) {