Merge "BUG-432: remove type argument from Registration"
authorTony Tkacik <ttkacik@cisco.com>
Mon, 28 Jul 2014 08:54:21 +0000 (08:54 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 28 Jul 2014 08:54:21 +0000 (08:54 +0000)
29 files changed:
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/Immutables.java
common/util/pom.xml
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/MappingCheckedFuture.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/MappingCheckedFutureTest.java [new file with mode: 0644]
model/ietf/ietf-restconf/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilder.java
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/OperationFailedException.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNameModule.java
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResultBuilder.java
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/InstanceIdentifier.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/util/AbstractDocumentedDataNodeContainer.java

index d659123f9710e81a6fbc4c6df8ef3978e283c786..cc2d0186d4a03c5e7af8eaca829c3d278dc6a2cd 100644 (file)
@@ -16,7 +16,7 @@ import java.util.Set;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Mutable;
 
-public class Immutables {
+public final class Immutables {
 
     private Immutables() {
         throw new UnsupportedOperationException("Helper class");
@@ -25,7 +25,7 @@ public class Immutables {
     public static final Set<Class<?>> KNOWN_IMMUTABLES = Immutables.<Class<?>> asHashSet(
             //
             Integer.class, Short.class, BigDecimal.class, BigInteger.class, Byte.class, Character.class, Double.class,
-            Float.class);
+            Float.class, String.class);
 
     /**
      * Determines if object is known to be immutable
@@ -37,7 +37,7 @@ public class Immutables {
      *            Reference to check
      * @return true if object is known to be immutable false otherwise.
      */
-    public static boolean isImmutable(Object o) {
+    public static boolean isImmutable(final Object o) {
         if (o == null) {
             throw new IllegalArgumentException("Object should not be null");
         }
@@ -54,7 +54,7 @@ public class Immutables {
     }
 
     @SafeVarargs
-    private static <E> Set<E> asHashSet(E... list) {
+    private static <E> Set<E> asHashSet(final E... list) {
         HashSet<E> ret = new HashSet<>();
         for (E e : list) {
             ret.add(e);
index 64db1287bf9792403202eab7b3468884160404e0..d0ab7fc85e167ceda45d5d9b3a2474f6989c1415 100644 (file)
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <Export-Package>org.opendaylight.yangtools.util</Export-Package>
+                        <Export-Package>
+                          org.opendaylight.yangtools.util,
+                          org.opendaylight.yangtools.util.concurrent
+                        </Export-Package>
                         <Embed-Dependency>java-concurrent-hash-trie-map;inline=true</Embed-Dependency>
                     </instructions>
                 </configuration>
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java
new file mode 100644 (file)
index 0000000..39332be
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.util.concurrent;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AbstractListeningExecutorService;
+import com.google.common.util.concurrent.ForwardingListenableFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * An implementation of ListeningExecutorService that attempts to detect deadlock scenarios that
+ * could occur if clients invoke the returned Future's <ode>get</code> methods synchronously.
+ * <p>
+ * Deadlock scenarios are most apt to occur with a backing single-threaded executor where setting of
+ * the Future's result is executed on the single thread. Here's a scenario:
+ * <ul>
+ * <li>Client code is currently executing in an executor's single thread.</li>
+ * <li>The client submits another task to the same executor.</li>
+ * <li>The client calls <code>get()</code> synchronously on the returned Future</li>
+ * </ul>
+ * The second submitted task will never execute since the single thread is currently executing
+ * the client code which is blocked waiting for the submitted task to complete. Thus, deadlock has
+ * occurred.
+ * <p>
+ * This class prevents this scenario via the use of a ThreadLocal variable. When a task is invoked,
+ * the ThreadLocal is set and, when a task completes, the ThreadLocal is cleared. Futures returned
+ * from this class override the <code>get</code> methods to check if the ThreadLocal is set. If it is,
+ * an ExecutionException is thrown with a custom cause.
+ *
+ * @author Thomas Pantelis
+ */
+public class DeadlockDetectingListeningExecutorService extends AbstractListeningExecutorService {
+    private final ThreadLocal<Boolean> deadlockDetector = new ThreadLocal<>();
+    private final Function<Void, Exception> deadlockExceptionFunction;
+    private final ExecutorService delegate;
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing ExecutorService.
+     * @param deadlockExceptionFunction Function that returns an Exception instance to set as the
+     *             cause of the ExecutionException when a deadlock is detected.
+     */
+    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
+            final Function<Void,Exception> deadlockExceptionFunction) {
+        this.delegate = checkNotNull(delegate);
+        this.deadlockExceptionFunction = checkNotNull(deadlockExceptionFunction);
+    }
+
+    @Override
+    public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
+        return delegate.awaitTermination(timeout, unit);
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return delegate.isShutdown();
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return delegate.isTerminated();
+    }
+
+    @Override
+    public void shutdown() {
+        delegate.shutdown();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return delegate.shutdownNow();
+    }
+
+    @Override
+    public void execute(final Runnable command) {
+        delegate.execute(wrapRunnable(command));
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit(final Callable<T> task ) {
+        final ListenableFutureTask<T> futureTask = ListenableFutureTask.create(wrapCallable(task));
+        delegate.execute(futureTask);
+        return wrapListenableFuture(futureTask);
+    }
+
+    @Override
+    public ListenableFuture<?> submit( final Runnable task ) {
+        ListenableFutureTask<Void> futureTask = ListenableFutureTask.create(wrapRunnable(task), null);
+        delegate.execute(futureTask);
+        return wrapListenableFuture(futureTask);
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit(final Runnable task, final T result) {
+        ListenableFutureTask<T> futureTask = ListenableFutureTask.create(wrapRunnable(task), result);
+        delegate.execute(futureTask);
+        return wrapListenableFuture(futureTask);
+    }
+
+    private Runnable wrapRunnable(final Runnable task) {
+        return new Runnable() {
+            @Override
+            public void run() {
+                deadlockDetector.set(Boolean.TRUE);
+                try {
+                    task.run();
+                } finally {
+                    deadlockDetector.set(null);
+                }
+            }
+        };
+    }
+
+    private <T> Callable<T> wrapCallable(final Callable<T> delagate) {
+        return new Callable<T>() {
+            @Override
+            public T call() throws Exception {
+                deadlockDetector.set(Boolean.TRUE);
+                try {
+                    return delagate.call();
+                } finally {
+                    deadlockDetector.set(null);
+                }
+            }
+        };
+    }
+
+    private <T> ListenableFuture<T> wrapListenableFuture(final ListenableFuture<T> delegate ) {
+        /*
+         *  This creates a forwarding Future that overrides calls to get(...) to check, via the ThreadLocal,
+         * if the caller is doing a blocking call on a thread from this executor. If so, we detect this as
+         * a deadlock and throw an ExecutionException even though it may not be a deadlock if there are
+         * more than 1 thread in the pool. Either way, there's bad practice somewhere, either on the client
+         * side for doing a blocking call or in the framework's threading model.
+         */
+        return new ForwardingListenableFuture.SimpleForwardingListenableFuture<T>(delegate) {
+            @Override
+            public T get() throws InterruptedException, ExecutionException {
+                checkDeadLockDetectorTL();
+                return super.get();
+            }
+
+            @Override
+            public T get(final long timeout, final TimeUnit unit)
+                    throws InterruptedException, ExecutionException, TimeoutException {
+                checkDeadLockDetectorTL();
+                return super.get(timeout, unit);
+            }
+
+            void checkDeadLockDetectorTL() throws ExecutionException {
+                if (deadlockDetector.get() != null) {
+                    throw new ExecutionException("A potential deadlock was detected.",
+                            deadlockExceptionFunction.apply(null));
+                }
+            }
+        };
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java
new file mode 100644 (file)
index 0000000..af51032
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.util.concurrent;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Utility exception mapper which translates an Exception to a specified type of Exception.
+ *
+ * This mapper is intended to be used with {@link com.google.common.util.concurrent.Futures#makeChecked(com.google.common.util.concurrent.ListenableFuture, Function)}
+ * <ul>
+ * <li>if exception is the specified type or one of its subclasses, it returns original exception.
+ * <li>if exception is {@link ExecutionException} and the cause is of the specified type, it returns the cause
+ * <li>otherwise returns an instance of the specified exception type with original exception as the cause.
+ * </ul>
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <X> the exception type
+ */
+public abstract class ExceptionMapper<X extends Exception> implements Function<Exception, X> {
+    private final Class<X> exceptionType;
+    private final String opName;
+
+    /**
+     * Constructor.
+     *
+     * @param opName the String prefix for exception messages.
+     * @param exceptionType the exception type to which to translate.
+     */
+    public ExceptionMapper(final String opName, final Class<X> exceptionType) {
+        this.exceptionType = Preconditions.checkNotNull(exceptionType);
+        this.opName = Preconditions.checkNotNull(opName);
+    }
+
+    /**
+     * Invoked to create a new exception instance of the specified type.
+     *
+     * @param message the message for the new exception.
+     * @param cause the cause for the new exception.
+     *
+     * @return an instance of the exception type.
+     */
+    protected abstract X newWithCause(String message, Throwable cause);
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public X apply(final Exception e) {
+
+        // If exception is of the specified type,return it.
+        if (exceptionType.isAssignableFrom( e.getClass())) {
+            return (X) e;
+        }
+
+        // If exception is ExecutionException whose cause is of the specified
+        // type, return the cause.
+        if (e instanceof ExecutionException && e.getCause() != null) {
+            if (exceptionType.isAssignableFrom( e.getCause().getClass())) {
+                return (X) e.getCause();
+            } else {
+                return newWithCause(opName + " execution failed", e.getCause());
+            }
+        }
+
+        // Otherwise return an instance of the specified type with the original
+        // cause.
+
+        if (e instanceof InterruptedException) {
+            return newWithCause( opName + " was interupted.", e);
+        }
+
+        if (e instanceof CancellationException ) {
+            return newWithCause( opName + " was cancelled.", e);
+        }
+
+        // We really shouldn't get here but need to cover it anyway for completeness.
+        return newWithCause(opName + " encountered an unexpected failure", e);
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/MappingCheckedFuture.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/MappingCheckedFuture.java
new file mode 100644 (file)
index 0000000..48f69e2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.util.concurrent;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AbstractCheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An implementation of CheckedFuture that provides similar behavior for the <code>get</code> methods
+ * that the <code>checkedGet</code> methods provide.
+ * <p>
+ * For {@link CancellationException} and {@link InterruptedException}, the specified exception mapper
+ * is invoked to translate them to the checked exception type.
+ * <p>
+ * For {@link ExecutionException}, the mapper is invoked to translate the cause to the checked exception
+ * and a new ExecutionException is thrown with the translated cause.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <V> The result type returned by this Future's get method
+ * @param <X> The checked exception type
+ */
+public class MappingCheckedFuture<V, X extends Exception> extends AbstractCheckedFuture<V, X> {
+
+    private final Function<Exception, X> mapper;
+
+    private MappingCheckedFuture( ListenableFuture<V> delegate, Function<Exception, X> mapper ) {
+        super( delegate );
+        this.mapper = Preconditions.checkNotNull( mapper );
+    }
+
+    /**
+     * Creates a new <code>MappingCheckedFuture</code> that wraps the given {@link ListenableFuture}
+     * delegate.
+     *
+     * @param delegate the {@link ListenableFuture} to wrap
+     * @param mapper the mapping {@link Function} used to translate exceptions from the delegate
+     * @return a new <code>MappingCheckedFuture</code>
+     */
+    public static <V, X extends Exception> MappingCheckedFuture<V, X> create(
+            ListenableFuture<V> delegate, Function<Exception, X> mapper ) {
+        return new MappingCheckedFuture<V, X>( delegate, mapper );
+    }
+
+    @Override
+    protected X mapException( Exception e ) {
+        return mapper.apply( e );
+    }
+
+    private ExecutionException wrapInExecutionException( String message, final Exception e ) {
+        return new ExecutionException( message, mapException( e ) );
+    }
+
+    @Override
+    public V get() throws InterruptedException, ExecutionException {
+        try {
+            return super.get();
+        } catch( InterruptedException e ) {
+            Thread.currentThread().interrupt();
+            throw wrapInExecutionException( "Operation was interrupted", e );
+        } catch( CancellationException e ) {
+            throw wrapInExecutionException( "Operation was cancelled", e );
+        } catch( ExecutionException e ) {
+            throw wrapInExecutionException( e.getMessage(), e );
+        }
+    }
+
+    @Override
+    public V get( long timeout, TimeUnit unit )
+            throws InterruptedException, ExecutionException, TimeoutException {
+        try {
+            return super.get( timeout, unit );
+        } catch( InterruptedException e ) {
+            Thread.currentThread().interrupt();
+            throw wrapInExecutionException( "Operation was interrupted", e );
+        } catch( CancellationException e ) {
+            throw wrapInExecutionException( "Operation was cancelled", e );
+        } catch( ExecutionException e ) {
+            throw wrapInExecutionException( e.getMessage(), e );
+        }
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java
new file mode 100644 (file)
index 0000000..b23750d
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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.util.concurrent;
+
+import static org.junit.Assert.*;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ * Unit tests for DeadlockDetectingListeningExecutorService.
+ *
+ * @author Thomas Pantelis
+ */
+public class DeadlockDetectingListeningExecutorServiceTest {
+
+    interface Invoker {
+        ListenableFuture<?> invokeExecutor( ListeningExecutorService executor );
+    };
+
+    static final Invoker SUBMIT_CALLABLE = new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
+            return executor.submit( new Callable<String>() {
+                @Override
+                public String call() throws Exception{
+                    return "foo";
+                }
+            } );
+        }
+    };
+
+    static final Invoker SUBMIT_RUNNABLE =  new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
+            return executor.submit( new Runnable() {
+                @Override
+                public void run(){
+                }
+            } );
+        }
+    };
+
+    static final Invoker SUBMIT_RUNNABLE_WITH_RESULT = new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
+            return executor.submit( new Runnable() {
+                @Override
+                public void run(){
+                }
+            }, "foo" );
+        }
+    };
+
+    interface InitialInvoker {
+        void invokeExecutor( ListeningExecutorService executor, Runnable task );
+    };
+
+    static final InitialInvoker SUBMIT = new InitialInvoker() {
+        @Override
+        public void invokeExecutor( ListeningExecutorService executor, Runnable task ) {
+            executor.submit( task );
+        }
+    };
+
+    static final InitialInvoker EXECUTE = new InitialInvoker() {
+        @Override
+        public void invokeExecutor( ListeningExecutorService executor, Runnable task ) {
+            executor.execute( task );
+        }
+    };
+
+    @SuppressWarnings("serial")
+    public static class TestDeadlockException extends Exception {
+    }
+
+    public static Function<Void, Exception> DEADLOCK_EXECUTOR_FUNCTION = new Function<Void, Exception>() {
+        @Override
+        public Exception apply( Void notUsed ) {
+            return new TestDeadlockException();
+        }
+    };
+
+    DeadlockDetectingListeningExecutorService executor;
+
+    @Before
+    public void setup() {
+        executor = new DeadlockDetectingListeningExecutorService( Executors.newSingleThreadExecutor(),
+                                                                  DEADLOCK_EXECUTOR_FUNCTION );
+    }
+
+    @Test
+    public void testBlockingSubmitOffExecutor() throws Exception {
+
+        // Test submit with Callable.
+
+        ListenableFuture<String> future = executor.submit( new Callable<String>() {
+            @Override
+            public String call() throws Exception{
+                return "foo";
+            }
+        } );
+
+        assertEquals( "Future result", "foo", future.get( 5, TimeUnit.SECONDS ) );
+
+        // Test submit with Runnable.
+
+        executor.submit( new Runnable() {
+            @Override
+            public void run(){
+            }
+        } ).get();
+
+        // Test submit with Runnable and value.
+
+        future = executor.submit( new Runnable() {
+            @Override
+            public void run(){
+            }
+        }, "foo" );
+
+        assertEquals( "Future result", "foo", future.get( 5, TimeUnit.SECONDS ) );
+    }
+
+    @Test
+    public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
+
+        testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
+        testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
+        testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
+
+        testNonBlockingSubmitOnExecutorThread( EXECUTE, SUBMIT_CALLABLE );
+    }
+
+    void testNonBlockingSubmitOnExecutorThread( InitialInvoker initialInvoker,
+                                                final Invoker invoker ) throws Throwable {
+
+        final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+        final CountDownLatch futureCompletedLatch = new CountDownLatch( 1 );
+
+        Runnable task = new Runnable() {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            @Override
+            public void run() {
+
+                Futures.addCallback( invoker.invokeExecutor( executor ), new FutureCallback() {
+                    @Override
+                    public void onSuccess( Object result ) {
+                        futureCompletedLatch.countDown();
+                    }
+
+                    @Override
+                    public void onFailure( Throwable t ) {
+                        caughtEx.set( t );
+                        futureCompletedLatch.countDown();
+                    }
+                } );
+            }
+
+        };
+
+        initialInvoker.invokeExecutor( executor, task );
+
+        assertTrue( "Task did not complete - executor likely deadlocked",
+                    futureCompletedLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( caughtEx.get() != null ) {
+            throw caughtEx.get();
+        }
+    }
+
+    @Test
+    public void testBlockingSubmitOnExecutorThread() throws Exception {
+
+        testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
+        testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
+        testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
+
+        testBlockingSubmitOnExecutorThread( EXECUTE, SUBMIT_CALLABLE );
+    }
+
+    void testBlockingSubmitOnExecutorThread( InitialInvoker initialInvoker,
+                                             final Invoker invoker ) throws Exception {
+
+        final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+        final CountDownLatch latch = new CountDownLatch( 1 );
+
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+
+                try {
+                    invoker.invokeExecutor( executor ).get();
+                } catch( ExecutionException e ) {
+                    caughtEx.set( e.getCause() );
+                } catch( Throwable e ) {
+                    caughtEx.set( e );
+                } finally {
+                    latch.countDown();
+                }
+            }
+
+        };
+
+        initialInvoker.invokeExecutor( executor, task );
+
+        assertTrue( "Task did not complete - executor likely deadlocked",
+                    latch.await( 5, TimeUnit.SECONDS ) );
+
+        assertNotNull( "Expected exception thrown", caughtEx.get() );
+        assertEquals( "Caught exception type", TestDeadlockException.class, caughtEx.get().getClass() );
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/MappingCheckedFutureTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/MappingCheckedFutureTest.java
new file mode 100644 (file)
index 0000000..45b9e3c
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * 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.util.concurrent;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Test;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Unit tests for MappingCheckedFuture.
+ *
+ * @author Thomas Pantelis
+ */
+public class MappingCheckedFutureTest {
+
+    interface FutureInvoker {
+        void invokeGet( CheckedFuture<?,?> future ) throws Exception;
+        Throwable extractWrappedTestEx( Exception from );
+    }
+
+    @SuppressWarnings("serial")
+    static class TestException extends Exception {
+        TestException( String message, Throwable cause ) {
+            super( message, cause );
+        }
+    }
+
+    static final ExceptionMapper<TestException> MAPPER =  new ExceptionMapper<TestException>(
+                                                                      "Test", TestException.class ) {
+
+        @Override
+        protected TestException newWithCause( String message, Throwable cause ) {
+            return new TestException( message, cause );
+        }
+    };
+
+    static final FutureInvoker GET = new FutureInvoker() {
+        @Override
+        public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+            future.get();
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx( Exception from ) {
+            if( from instanceof ExecutionException ) {
+                return ((ExecutionException)from).getCause();
+            }
+
+            return from;
+        }
+    };
+
+    static final FutureInvoker TIMED_GET = new FutureInvoker() {
+        @Override
+        public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+            future.get( 1, TimeUnit.HOURS );
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx( Exception from ) {
+            if( from instanceof ExecutionException ) {
+                return ((ExecutionException)from).getCause();
+            }
+
+            return from;
+        }
+    };
+
+    static final FutureInvoker CHECKED_GET = new FutureInvoker() {
+        @Override
+        public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+            future.checkedGet();
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx( Exception from ) {
+            return from;
+        }
+    };
+
+    static final FutureInvoker TIMED_CHECKED_GET = new FutureInvoker() {
+        @Override
+        public void invokeGet( CheckedFuture<?,?> future ) throws Exception {
+            future.checkedGet( 50, TimeUnit.MILLISECONDS );
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx( Exception from ) {
+            return from;
+        }
+    };
+
+    @Test
+    public void testGet() throws Exception {
+
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String,TestException> future = MappingCheckedFuture.create( delegate, MAPPER );
+        delegate.set( "test" );
+        assertEquals( "get", "test", future.get() );
+    }
+
+    @Test
+    public void testGetWithExceptions() throws Exception {
+
+        testExecutionException( GET, new RuntimeException() );
+        testExecutionException( GET, new TestException( "mock", null ) );
+        testCancellationException( GET );
+        testInterruptedException( GET );
+    }
+
+    @Test
+    public void testTimedGet() throws Exception {
+
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String,TestException> future = MappingCheckedFuture.create( delegate, MAPPER );
+        delegate.set( "test" );
+        assertEquals( "get", "test", future.get( 50, TimeUnit.MILLISECONDS ) );
+    }
+
+    @Test
+    public void testTimedGetWithExceptions() throws Exception {
+
+        testExecutionException( TIMED_GET, new RuntimeException() );
+        testCancellationException( TIMED_GET );
+        testInterruptedException( TIMED_GET );
+    }
+
+    @Test
+    public void testCheckedGetWithExceptions() throws Exception {
+
+        testExecutionException( CHECKED_GET, new RuntimeException() );
+        testCancellationException( CHECKED_GET );
+        testInterruptedException( CHECKED_GET );
+    }
+
+    @Test
+    public void testTimedCheckedWithExceptions() throws Exception {
+
+        testExecutionException( TIMED_CHECKED_GET, new RuntimeException() );
+        testCancellationException( TIMED_CHECKED_GET );
+        testInterruptedException( TIMED_CHECKED_GET );
+    }
+
+    private void testExecutionException( FutureInvoker invoker, Throwable cause ) {
+
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String,TestException> mappingFuture =
+                                                        MappingCheckedFuture.create( delegate, MAPPER );
+
+        delegate.setException( cause );
+
+        try {
+            invoker.invokeGet( mappingFuture );
+            fail( "Expected exception thrown" );
+        } catch( Exception e ) {
+            Throwable expectedTestEx = invoker.extractWrappedTestEx( e );
+            assertNotNull( "Expected returned exception is null", expectedTestEx );
+            assertEquals( "Exception type", TestException.class, expectedTestEx.getClass() );
+
+            if( cause instanceof TestException ) {
+                assertNull( "Expected null cause", expectedTestEx.getCause() );
+            } else {
+                assertSame( "TestException cause", cause, expectedTestEx.getCause() );
+            }
+        }
+    }
+
+    private void testCancellationException( FutureInvoker invoker ) {
+
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String,TestException> mappingFuture =
+                                                        MappingCheckedFuture.create( delegate, MAPPER );
+
+        mappingFuture.cancel( false );
+
+        try {
+            invoker.invokeGet( mappingFuture );
+            fail( "Expected exception thrown" );
+        } catch( Exception e ) {
+            Throwable expectedTestEx = invoker.extractWrappedTestEx( e );
+            assertNotNull( "Expected returned exception is null", expectedTestEx );
+            assertEquals( "Exception type", TestException.class, expectedTestEx.getClass() );
+            assertEquals( "TestException cause type", CancellationException.class,
+                          expectedTestEx.getCause().getClass() );
+        }
+    }
+
+    private void testInterruptedException( final FutureInvoker invoker ) throws Exception {
+
+        SettableFuture<String> delegate = SettableFuture.create();
+        final MappingCheckedFuture<String,TestException> mappingFuture =
+                                                        MappingCheckedFuture.create( delegate, MAPPER );
+
+        final AtomicReference<AssertionError> assertError = new AtomicReference<>();
+        final CountDownLatch doneLatch = new CountDownLatch( 1 );
+        Thread thread = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    doInvoke();
+                } catch( AssertionError e ) {
+                    assertError.set( e );
+                } finally {
+                    doneLatch.countDown();
+                }
+            }
+
+            void doInvoke() {
+                try {
+                    invoker.invokeGet( mappingFuture );
+                    fail( "Expected exception thrown" );
+                } catch( Exception e ) {
+                    Throwable expectedTestEx = invoker.extractWrappedTestEx( e );
+                    assertNotNull( "Expected returned exception is null", expectedTestEx );
+                    assertEquals( "Exception type", TestException.class, expectedTestEx.getClass() );
+                    assertEquals( "TestException cause type", InterruptedException.class,
+                                  expectedTestEx.getCause().getClass() );
+                }
+            }
+        };
+        thread.start();
+
+        thread.interrupt();
+        assertEquals( "get call completed", true, doneLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( assertError.get() != null ) {
+            throw assertError.get();
+        }
+    }
+}
index 35b84e3462e534c0623cef1106a49d224a023220..42defe53ca2cbba21db87cb09b3041739a9995d3 100644 (file)
@@ -1,20 +1,20 @@
 /*
-* 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
-*/
+ * 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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.DatastoreIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.DatastoreIdentifier.Enumeration;
 
 
 /**
-**/
+ **/
 public class DatastoreIdentifierBuilder {
 
-    public static DatastoreIdentifier getDefaultInstance(String defaultValue) {
-        throw new UnsupportedOperationException("Not yet implemented");
+    public static DatastoreIdentifier getDefaultInstance(final String defaultValue) {
+        return new DatastoreIdentifier(Enumeration.valueOf(defaultValue));
     }
 
 }
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/OperationFailedException.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/OperationFailedException.java
new file mode 100644 (file)
index 0000000..e1f0b71
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.Arrays;
+import java.util.List;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A general base exception for an operation failure.
+ *
+ * @author Thomas Pantelis
+ */
+public class OperationFailedException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    private final List<RpcError> errorList;
+
+    /**
+     * Constructs a new instance with the specified detail message and errors.
+     *
+     * @param message the detail message
+     * @param errors {@link RpcError} instances that provide additional error information about
+     *               this exception
+     */
+    public OperationFailedException(final String message, final RpcError... errors) {
+        this(message, null, errors);
+    }
+
+    /**
+     * Constructs a new instance with the specified detail message, cause and errors.
+     *
+     * @param message the detail message
+     * @param cause the cause
+     * @param errors {@link RpcError} instances that provide additional error information about
+     *               this exception
+     */
+    public OperationFailedException(final String message, final Throwable cause,
+                                    final RpcError... errors) {
+        super(Preconditions.checkNotNull(message), cause);
+
+        if( errors != null && errors.length > 0 ) {
+            errorList = ImmutableList.<RpcError>copyOf( Arrays.asList( errors ) );
+        }
+        else {
+            // Add a default RpcError.
+            errorList = ImmutableList.of(RpcResultBuilder.newError(ErrorType.APPLICATION, null,
+                    getMessage(), null, null, getCause()));
+        }
+    }
+
+    /**
+     * Returns additional error information about this exception.
+     *
+     * @return a List of RpcErrors. There is always at least one RpcError.
+     */
+    public List<RpcError> getErrorList() {
+        return errorList;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper( this ).add( "message", getMessage() )
+                .add( "errorList", errorList ).toString();
+    }
+}
index 4afa8c7c98f007d24110ea6f256360f702ea79fa..a45c91b923b13c8ddb26fb5e81ad16d12d54f9cc 100644 (file)
@@ -98,18 +98,18 @@ public final class QNameModule implements Immutable, Serializable {
             return false;
         }
         final QNameModule other = (QNameModule) obj;
-        if (namespace == null) {
-            if (other.namespace != null) {
+        if (revision == null) {
+            if (other.revision != null) {
                 return false;
             }
-        } else if (!namespace.equals(other.namespace)) {
+        } else if (!revision.equals(other.revision)) {
             return false;
         }
-        if (revision == null) {
-            if (other.revision != null) {
+        if (namespace == null) {
+            if (other.namespace != null) {
                 return false;
             }
-        } else if (!revision.equals(other.revision)) {
+        } else if (!namespace.equals(other.namespace)) {
             return false;
         }
         return true;
index ab358d8c1a8653bd3c716e557ceebe16bb1354d5..05477e818f7a586789c7d4222aaed1f99f37b714 100644 (file)
@@ -188,7 +188,8 @@ public final class RpcResultBuilder<T> {
      * @return an RpcError
      */
     public static RpcError newError( ErrorType errorType, String tag, String message ) {
-        return new RpcErrorImpl( ErrorSeverity.ERROR, errorType, tag, message, null, null, null );
+        return new RpcErrorImpl( ErrorSeverity.ERROR, errorType,
+                tag != null ? tag : "operation-failed", message, null, null, null );
     }
 
     /**
@@ -207,8 +208,8 @@ public final class RpcResultBuilder<T> {
      */
     public static RpcError newError(  ErrorType errorType, String tag, String message,
             String applicationTag, String info, Throwable cause ) {
-        return new RpcErrorImpl( ErrorSeverity.ERROR, errorType, tag, message,
-                                 applicationTag, info, cause );
+        return new RpcErrorImpl( ErrorSeverity.ERROR, errorType,
+                tag != null ? tag : "operation-failed", message, applicationTag, info, cause );
     }
 
     /**
index b864f8a74c99503ceaf1e3e858883592ebd3c6a5..edcf46b867df58e57eaed73eb043869b5a54cc1c 100644 (file)
@@ -12,10 +12,13 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 
 import java.io.Serializable;
 import java.lang.reflect.Array;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -26,13 +29,13 @@ import java.util.Set;
 import org.opendaylight.yangtools.concepts.Builder;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 
 /**
  * Unique identifier of a partical node instance in the data tree.
  *
- *
  * <p>
  * Java representation of YANG Built-in type <code>instance-identifier</code>,
  * which conceptually is XPath expression minimised to uniquely identify element
@@ -61,19 +64,30 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
  *
  *
  * @see http://tools.ietf.org/html/rfc6020#section-9.13
- *
- *
  */
-public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
+public final class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
+    private static final InstanceIdentifier EMPTY = trustedCreate(Collections.<PathArgument>emptyList());
 
-    private static final long serialVersionUID = 8467409862384206193L;
-    private final ImmutableList<PathArgument> path;
+    private static final long serialVersionUID = 2L;
+    private final Iterable<PathArgument> pathArguments;
+    private final int hash;
 
+    private transient ImmutableList<PathArgument> legacyPath = null;
     private transient String toStringCache = null;
-    private transient Integer hashCodeCache = null;
+
+    private final ImmutableList<PathArgument> getLegacyPath() {
+        if (legacyPath == null) {
+            synchronized (this) {
+                if (legacyPath == null) {
+                    legacyPath = ImmutableList.copyOf(pathArguments);
+                }
+            }
+        }
+
+        return legacyPath;
+    }
 
     /**
-     *
      * Returns a list of path arguments.
      *
      * @deprecated Use {@link #getPathArguments()} instead.
@@ -81,7 +95,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      */
     @Deprecated
     public List<PathArgument> getPath() {
-        return path;
+        return getLegacyPath();
     }
 
     /**
@@ -90,7 +104,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * @return Immutable iteration of path arguments.
      */
     public Iterable<PathArgument> getPathArguments() {
-        return path;
+        return pathArguments;
     }
 
     /**
@@ -100,7 +114,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * @return Immutable iterable of path arguments in reverse order.
      */
     public Iterable<PathArgument> getReversePathArguments() {
-        return path.reverse();
+        return getLegacyPath().reverse();
     }
 
     /**
@@ -110,55 +124,46 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * @return The last past argument, or null if there are no PathArguments.
      */
     public PathArgument getLastPathArgument() {
-        return Iterables.getFirst(path.reverse(), null);
+        return Iterables.getFirst(getReversePathArguments(), null);
     }
 
-    /**
-     *
-     *
-     * @deprecated Use {@link #create(Iterable)} instead.
-     * @param path
-     */
-    @Deprecated
-    public InstanceIdentifier(final List<? extends PathArgument> path) {
-        this.path = ImmutableList.copyOf(path);
+    private InstanceIdentifier(final Iterable<PathArgument> path, final int hash) {
+        this.pathArguments = Preconditions.checkNotNull(path, "path must not be null.");
+        this.hash = hash;
     }
 
-    private InstanceIdentifier(final Iterable<? extends PathArgument> path) {
-        Preconditions.checkNotNull(path, "path must not be null.");
-        this.path = ImmutableList.copyOf(path);
-    }
+    private static final InstanceIdentifier trustedCreate(final Iterable<PathArgument> path) {
+        final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
+        for (PathArgument a : path) {
+            hash.addArgument(a);
+        }
 
-    private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
-        this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
+        return new InstanceIdentifier(path, hash.toInstance());
     }
 
     public static final InstanceIdentifier create(final Iterable<? extends PathArgument> path) {
-        return new InstanceIdentifier(path);
+        if (Iterables.isEmpty(path)) {
+            return EMPTY;
+        }
+
+        return trustedCreate(ImmutableList.copyOf(path));
     }
 
     public static final InstanceIdentifier create(final PathArgument... path) {
+        // We are forcing a copy, since we cannot trust the user
         return create(Arrays.asList(path));
     }
 
     @Override
     public int hashCode() {
         /*
-         * The hashCodeCache is safe, since the object contract requires
+         * The caching is safe, since the object contract requires
          * immutability of the object and all objects referenced from this
          * object.
          * Used lists, maps are immutable. Path Arguments (elements) are also
          * immutable, since the PathArgument contract requires immutability.
-         * The cache is thread-safe - if multiple computations occurs at the
-         * same time, cache will be overwritten with same result.
          */
-        if (hashCodeCache == null) {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + path.hashCode();
-            hashCodeCache = result;
-        }
-        return hashCodeCache;
+        return hash;
     }
 
     @Override
@@ -176,18 +181,10 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         if (this.hashCode() != obj.hashCode()) {
             return false;
         }
-        if (path == null) {
-            if (other.path != null) {
-                return false;
-            }
-        } else if (!path.equals(other.path)) {
-            return false;
-        }
-        return true;
+        return Iterables.elementsEqual(pathArguments, other.pathArguments);
     }
 
     /**
-     *
      * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments
      *
      * @param name QName of {@link NodeIdentifier}
@@ -205,7 +202,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * @return Instance Identifier with additional path argument added to the end.
      */
     public InstanceIdentifier node(final PathArgument arg) {
-        return create(ImmutableList.<PathArgument> builder().addAll(path).add(arg).build());
+        return new InstanceIdentifier(Iterables.concat(pathArguments, Collections.singleton(arg)), HashCodeBuilder.nextHashCode(hash, arg));
     }
 
     /**
@@ -218,15 +215,29 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      *         the specified parent is not in fact an ancestor of this object.
      */
     public Optional<InstanceIdentifier> relativeTo(final InstanceIdentifier ancestor) {
-        if (ancestor.contains(this)) {
-            final int common = ancestor.path.size();
-            return Optional.of(new InstanceIdentifier(Iterables.skip(path, common)));
-        } else {
-            return Optional.absent();
+        final Iterator<?> lit = pathArguments.iterator();
+        final Iterator<?> oit = ancestor.pathArguments.iterator();
+        int common = 0;
+
+        while (oit.hasNext()) {
+            // Ancestor is not really an ancestor
+            if (!lit.hasNext() || !lit.next().equals(oit.next())) {
+                return Optional.absent();
+            }
+
+            ++common;
+        }
+
+        if (common == 0) {
+            return Optional.of(this);
+        }
+        if (!lit.hasNext()) {
+            return Optional.of(EMPTY);
         }
+        return Optional.of(trustedCreate(Iterables.skip(pathArguments, common)));
     }
 
-    static int hashCode(final Object value) {
+    private static int hashCode(final Object value) {
         if (value == null) {
             return 0;
         }
@@ -258,7 +269,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
      */
     public static InstanceIdentifier of(final QName name) {
-        return new InstanceIdentifier(new NodeIdentifier(name));
+        return create(new NodeIdentifier(name));
     }
 
     /**
@@ -279,21 +290,23 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
      */
     static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
-        return new BuilderImpl(origin.getPath());
+        return new BuilderImpl(origin.getPathArguments(), origin.hashCode());
     }
+
     /**
-     *
      * Returns new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
      *
      * @param node QName of first {@link NodeIdentifier} path argument.
      * @return  new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
+     *
+     * @deprecated Either use {@link #node(QName)} or instantiate an intermediate builder.
      */
+    @Deprecated
     public static InstanceIdentifierBuilder builder(final QName node) {
         return builder().node(node);
     }
 
     /**
-     *
      * Path argument / component of InstanceIdentifier
      *
      * Path argument uniquelly identifies node in data tree on particular
@@ -312,12 +325,8 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      * <li>{@link AugmentationIdentifier} - Identifier of augmentation
      * <li>{@link NodeWithValue} - Identifier of leaf-list entry
      * </ul>
-     *
-     *
-     *
      */
     public interface PathArgument extends Comparable<PathArgument>, Immutable, Serializable {
-
         /**
          * If applicable returns unique QName of data node as defined in YANG
          * Schema.
@@ -325,22 +334,21 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
          * This method may return null, if the corresponding schema node, does
          * not have QName associated, such as in cases of augmentations.
          *
-         * @return
+         * @return Node type
          */
         QName getNodeType();
-
     }
 
     private static abstract class AbstractPathArgument implements PathArgument {
         private static final long serialVersionUID = -4546547994250849340L;
-        protected final QName nodeType;
+        private final QName nodeType;
 
         protected AbstractPathArgument(final QName nodeType) {
             this.nodeType = Preconditions.checkNotNull(nodeType);
         }
 
         @Override
-        public QName getNodeType() {
+        public final QName getNodeType() {
             return nodeType;
         }
 
@@ -349,6 +357,27 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
             return nodeType.compareTo(o.getNodeType());
         }
 
+        @Override
+        public int hashCode() {
+            return 31 + getNodeType().hashCode();
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || this.getClass() != obj.getClass()) {
+                return false;
+            }
+
+            return getNodeType().equals(((AbstractPathArgument)obj).getNodeType());
+        }
+
+        @Override
+        public String toString() {
+            return getNodeType().toString();
+        }
     }
 
     /**
@@ -359,9 +388,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
      *
      */
     public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
-
         /**
-         *
          * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
          *
          * @param nodeType QName of {@link NodeIdentifier} which will be added
@@ -370,7 +397,6 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         InstanceIdentifierBuilder node(QName nodeType);
 
         /**
-         *
          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting instance identifier.
          *
          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
@@ -380,7 +406,6 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
 
         /**
-         *
          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
          *
          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
@@ -390,15 +415,6 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
          */
         InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
 
-        /**
-         *
-         * @return
-         * @deprecated use {@link #build()}
-         *
-         */
-        @Deprecated
-        InstanceIdentifier getIdentifier();
-
         /**
          *
          * Builds an {@link InstanceIdentifier} with path arguments from this builder
@@ -418,29 +434,6 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         public NodeIdentifier(final QName node) {
             super(node);
         }
-
-        @Override
-        public int hashCode() {
-            return 31 + nodeType.hashCode();
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (!(obj instanceof NodeIdentifier)) {
-                return false;
-            }
-            final NodeIdentifier other = (NodeIdentifier) obj;
-            return nodeType.equals(other.nodeType);
-        }
-
-        @Override
-        public String toString() {
-            return nodeType.toString();
-        }
-
     }
 
     /**
@@ -461,11 +454,6 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
             this(node, ImmutableMap.of(key, value));
         }
 
-        @Override
-        public QName getNodeType() {
-            return nodeType;
-        }
-
         public Map<QName, Object> getKeyValues() {
             return keyValues;
         }
@@ -473,71 +461,40 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         @Override
         public int hashCode() {
             final int prime = 31;
-            int result = 1;
-            result = prime * result + ((keyValues == null) ? 0 : hashKeyValues());
-            result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
-            return result;
-        }
+            int result = super.hashCode();
+            result = prime * result;
 
-        private int hashKeyValues() {
-            int hash = 0;
             for (Entry<QName, Object> entry : keyValues.entrySet()) {
-                hash += Objects.hashCode(entry.getKey()) + InstanceIdentifier.hashCode(entry.getValue());
+                result += Objects.hashCode(entry.getKey()) + InstanceIdentifier.hashCode(entry.getValue());
             }
-
-            return hash;
+            return result;
         }
 
         @Override
         public boolean equals(final Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
+            if (!super.equals(obj)) {
                 return false;
             }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
-            if (keyValues == null) {
-                if (other.keyValues != null) {
-                    return false;
-                }
-            } else if (!keyValuesEquals(other.keyValues)) {
-                return false;
-            }
-            if (nodeType == null) {
-                if (other.nodeType != null) {
-                    return false;
-                }
-            } else if (!nodeType.equals(other.nodeType)) {
-                return false;
-            }
-            return true;
-        }
 
-        private boolean keyValuesEquals(final Map<QName, Object> otherKeyValues) {
-            if (otherKeyValues == null || keyValues.size() != otherKeyValues.size()) {
+            final Map<QName, Object> otherKeyValues = ((NodeIdentifierWithPredicates) obj).keyValues;
+            if (keyValues.size() != otherKeyValues.size()) {
                 return false;
             }
 
-            boolean result = true;
             for (Entry<QName, Object> entry : keyValues.entrySet()) {
                 if (!otherKeyValues.containsKey(entry.getKey())
                         || !Objects.deepEquals(entry.getValue(), otherKeyValues.get(entry.getKey()))) {
 
-                    result = false;
-                    break;
+                    return false;
                 }
             }
 
-            return result;
+            return true;
         }
 
         @Override
         public String toString() {
-            return nodeType + "[" + keyValues + "]";
+            return super.toString() + '[' + keyValues + ']';
         }
     }
 
@@ -555,11 +512,6 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
             this.value = value;
         }
 
-        @Override
-        public QName getNodeType() {
-            return nodeType;
-        }
-
         public Object getValue() {
             return value;
         }
@@ -567,32 +519,24 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         @Override
         public int hashCode() {
             final int prime = 31;
-            int result = 1;
+            int result = super.hashCode();
             result = prime * result + ((value == null) ? 0 : InstanceIdentifier.hashCode(value));
-            result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
             return result;
         }
 
         @Override
         public boolean equals(final Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
+            if (!super.equals(obj)) {
                 return false;
             }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            NodeWithValue other = (NodeWithValue) obj;
-            return Objects.deepEquals(value, other.value) && Objects.equals(nodeType, other.nodeType);
+            final NodeWithValue other = (NodeWithValue) obj;
+            return Objects.deepEquals(value, other.value);
         }
 
         @Override
         public String toString() {
-            return nodeType + "[" + value + "]";
+            return super.toString() + '[' + value + ']';
         }
-
     }
 
     /**
@@ -656,8 +600,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
         @Override
         public String toString() {
             final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
-            sb.append("childNames=").append(childNames);
-            sb.append('}');
+            sb.append("childNames=").append(childNames).append('}');
             return sb.toString();
         }
 
@@ -671,12 +614,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
             }
 
             AugmentationIdentifier that = (AugmentationIdentifier) o;
-
-            if (!childNames.equals(that.childNames)) {
-                return false;
-            }
-
-            return true;
+            return childNames.equals(that.childNames);
         }
 
         @Override
@@ -711,33 +649,40 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
     }
 
     private static class BuilderImpl implements InstanceIdentifierBuilder {
-
-        private final ImmutableList.Builder<PathArgument> path;
+        private final HashCodeBuilder<PathArgument> hash;
+        private final List<PathArgument> path;
 
         public BuilderImpl() {
-            path = ImmutableList.<PathArgument> builder();
+            this.hash = new HashCodeBuilder<>();
+            this.path = new ArrayList<>();
         }
 
-        public BuilderImpl(final List<? extends PathArgument> prefix) {
-            path = ImmutableList.<PathArgument> builder();
-            path.addAll(prefix);
+        public BuilderImpl(final Iterable<PathArgument> prefix, final int hash) {
+            this.path = Lists.newArrayList(prefix);
+            this.hash = new HashCodeBuilder<>(hash);
         }
 
         @Override
         public InstanceIdentifierBuilder node(final QName nodeType) {
-            path.add(new NodeIdentifier(nodeType));
+            final PathArgument arg = new NodeIdentifier(nodeType);
+            path.add(arg);
+            hash.addArgument(arg);
             return this;
         }
 
         @Override
         public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
-            path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
+            final PathArgument arg = new NodeIdentifierWithPredicates(nodeType, key, value);
+            path.add(arg);
+            hash.addArgument(arg);
             return this;
         }
 
         @Override
         public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
-            path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
+            final PathArgument arg = new NodeIdentifierWithPredicates(nodeType, keyValues);
+            path.add(arg);
+            hash.addArgument(arg);
             return this;
         }
 
@@ -749,31 +694,27 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
 
         @Override
         public InstanceIdentifier build() {
-            return new InstanceIdentifier(path.build());
-        }
-
-        @Override
-        @Deprecated
-        public InstanceIdentifier getIdentifier() {
-            return build();
+            return new InstanceIdentifier(ImmutableList.copyOf(path), hash.toInstance());
         }
     }
 
     @Override
     public boolean contains(final InstanceIdentifier other) {
-        if (other == null) {
-            throw new IllegalArgumentException("other should not be null");
-        }
-        final int localSize = this.path.size();
-        final List<PathArgument> otherPath = other.getPath();
-        if (localSize > other.path.size()) {
-            return false;
-        }
-        for (int i = 0; i < localSize; i++) {
-            if (!path.get(i).equals(otherPath.get(i))) {
+        Preconditions.checkArgument(other != null, "other should not be null");
+
+        final Iterator<?> lit = pathArguments.iterator();
+        final Iterator<?> oit = other.pathArguments.iterator();
+
+        while (lit.hasNext()) {
+            if (!oit.hasNext()) {
+                return false;
+            }
+
+            if (!lit.next().equals(oit.next())) {
                 return false;
             }
         }
+
         return true;
     }
 
@@ -794,7 +735,7 @@ public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable,
 
         final StringBuilder builder = new StringBuilder('/');
         boolean first = true;
-        for (PathArgument argument : path) {
+        for (PathArgument argument : getPathArguments()) {
             if (first) {
                 first = false;
             } else {
index 438b05c7da3af2bab1424fba7e56b00c80a007e7..b2abac6cc7e3dbbd9dfcbb823f381a5e4812716c 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
 
+import com.google.common.base.Optional;
+
 import java.util.Collections;
 import java.util.Map;
 
@@ -15,19 +17,17 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-
-public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> //
-        extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>> //
-        implements Immutable, DataContainerNode<K> {
+public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>>
+implements Immutable, DataContainerNode<K> {
 
     protected final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
+    private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> publicChildren;
 
     public AbstractImmutableDataContainerNode(
             final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
         super(nodeIdentifier);
         this.children = children;
+        this.publicChildren = Collections.unmodifiableMap(children);
     }
 
     @Override
@@ -37,7 +37,7 @@ public abstract class AbstractImmutableDataContainerNode<K extends PathArgument>
 
     @Override
     public final Iterable<DataContainerChild<? extends PathArgument, ?>> getValue() {
-        return Iterables.unmodifiableIterable(children.values());
+        return publicChildren.values();
     }
 
     @Override
@@ -46,8 +46,7 @@ public abstract class AbstractImmutableDataContainerNode<K extends PathArgument>
     }
 
     public final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> getChildren() {
-        // Make sure we do not leak a mutable view
-        return Collections.unmodifiableMap(children);
+        return publicChildren;
     }
 
     @Override
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java
new file mode 100644 (file)
index 0000000..e101d9d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
+ */
+public final class AcceptingSchemaSourceFilter implements SchemaSourceFilter {
+    private static final AcceptingSchemaSourceFilter INSTANCE = new AcceptingSchemaSourceFilter();
+
+    private final Iterable<Class<? extends SchemaSourceRepresentation>> representations;
+
+    private AcceptingSchemaSourceFilter() {
+        final Builder<Class<? extends SchemaSourceRepresentation>> b = ImmutableList.builder();
+        b.add(SchemaSourceRepresentation.class);
+        representations = b.build();
+    }
+
+    /**
+     * Return the singleton instance of this filter.
+     *
+     * @return Singleton shared instance.
+     */
+    public static final AcceptingSchemaSourceFilter getSingletonInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
+        return representations;
+    }
+
+    @Override
+    public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
+        return Futures.immediateFuture(Boolean.TRUE);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java
new file mode 100644 (file)
index 0000000..8869c9e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * An asynchronous factory for building {@link SchemaContext} instances
+ * based on a specification of what {@link SourceIdentifier}s are required
+ * and dynamic recursive resolution.
+ */
+public interface SchemaContextFactory {
+    /**
+     * Create a new schema context containing specified sources, pulling in
+     * any dependencies they may have.
+     *
+     * @param requiredSources a collection of sources which are required to
+     *                        be present
+     * @return A checked future, which will produce a schema context, or
+     *         fail with an explanation why the creation of the schema context
+     *         failed.
+     */
+    CheckedFuture<SchemaContext, SchemaResolutionException> createSchemaContext(@Nonnull Collection<SourceIdentifier> requiredSources);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java
new file mode 100644 (file)
index 0000000..807bbf0
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Interface exposed by repository implementations. A schema repository is a logically
+ * centralized place for model storage and creation of {@link SchemaContext} instances.
+ */
+public interface SchemaRepository {
+    /**
+     * Instantiate a new {@link SchemaContextFactory}, which will filter available schema
+     * sources using the provided filter.
+     *
+     * @param filter Filter which acts as the gating function before a schema source is
+     *               considered by the factory for inclusion in the {@link SchemaContext}
+     *               it produces.
+     * @return A new schema context factory.
+     */
+    SchemaContextFactory createSchemaContextFactory(@Nonnull SchemaSourceFilter filter);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java
new file mode 100644 (file)
index 0000000..b47e366
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when a Schema Source fails to resolve.
+ */
+public class SchemaResolutionException extends Exception {
+    private static final long serialVersionUID = 1L;
+    private final Map<SourceIdentifier, Throwable> unresolvedSources;
+
+    public SchemaResolutionException(final @Nonnull String message) {
+        this(message, (Throwable)null);
+    }
+
+    public SchemaResolutionException(final @Nonnull String message, final Throwable cause) {
+        this(message, cause, ImmutableMap.<SourceIdentifier, Exception>of());
+    }
+
+    public SchemaResolutionException(final @Nonnull String message, final @Nonnull Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+        super(Preconditions.checkNotNull(message));
+        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+    }
+
+    public SchemaResolutionException(final @Nonnull String message, final Throwable cause, @Nonnull final Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+        super(message, cause);
+        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+    }
+
+    /**
+     * Return the list of sources which failed to resolve along with reasons
+     * why they were not resolved.
+     *
+     * @return Source/reason map.
+     */
+    public final Map<SourceIdentifier, Throwable> getUnresolvedSources() {
+        return unresolvedSources;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(Objects.toStringHelper(this).add("unresolvedSources", unresolvedSources)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java
new file mode 100644 (file)
index 0000000..188f83c
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface SchemaSourceFilter {
+    Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations();
+    ListenableFuture<Boolean> apply(SchemaSourceRepresentation schemaSource);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java
new file mode 100644 (file)
index 0000000..b1261a9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Common interface for schema source representations.
+ *
+ * A schema source is an atomic piece of the overall schema context. In YANG terms,
+ * a schema source is semantically equivalent to a single YANG text file, be it a
+ * module or a submodule.
+ *
+ * A schema source can exist in various forms, which we call representations. Again,
+ * in YANG terms, each representation is semantically equivalent, but from
+ * implementation perspective certain operations on a schema source may require it
+ * to be first transformed into a particular representation before they can be
+ * applied. Such transformations are affected via instances of
+ * {@link SchemaSourceTransformation}.
+ *
+ * Typical examples of a schema source representation include:
+ * <ul>
+ * <li>a {@link java.lang.String} - textual representation of source code
+ * <li>a {@link java.io.InputStream} - input stream containing source code
+ * <li>a {@link com.google.common.io.ByteSource} - source for input streams
+ * containing source code
+ * <li>Parsed abstract syntax tree (AST), which is the result of a syntactic parser
+ *
+ * Implementations of this interface expected to comply with the {@link Immutable}
+ * contract.
+ */
+public interface SchemaSourceRepresentation extends Identifiable<SourceIdentifier>, Immutable {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    SourceIdentifier getIdentifier();
+
+    /**
+     * Return the concrete representation type.
+     *
+     * @return The type of representation.
+     */
+    Class<? extends SchemaSourceRepresentation> getType();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java
new file mode 100644 (file)
index 0000000..cd3a0fb
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.Identifier;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ *
+ * YANG Schema source identifier
+ *
+ * Simple transfer object represents identifier of source for YANG schema (module or submodule),
+ * which consists of
+ * <ul>
+ * <li>YANG schema name ({@link #getName()}
+ * <li>Module revision (optional) ({link {@link #getRevision()})
+ * </ul>
+ *
+ * Source identifier is designated to be carry only necessary information
+ * to look-up YANG model source and to be used by {@link AdvancedSchemaSourceProvider}
+ * and similar.
+ *
+ * <b>Note:</b>On source retrieval layer it is impossible to distinguish
+ * between YANG module and/or submodule unless source is present.
+ *
+ * <p>
+ * (For further reference see: http://tools.ietf.org/html/rfc6020#section-5.2 and
+ * http://tools.ietf.org/html/rfc6022#section-3.1 ).
+ *
+ *
+ */
+public final class SourceIdentifier implements Identifier, Immutable {
+    private static final long serialVersionUID = 1L;
+    private final String revision;
+    private final String name;
+
+    /**
+     *
+     * Creates new YANG Schema source identifier.
+     *
+     * @param name Name of schema
+     * @param formattedRevision Revision of source in format YYYY-mm-dd
+     */
+    public SourceIdentifier(final String name, final Optional<String> formattedRevision) {
+        super();
+        this.name = Preconditions.checkNotNull(name);
+        this.revision = formattedRevision.orNull();
+    }
+
+    /**
+     * Returns model name
+     *
+     * @return model name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns revision of source or null if revision was not supplied.
+     *
+     * @return revision of source or null if revision was not supplied.
+     */
+    public String getRevision() {
+        return revision;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + ((revision == null) ? 0 : revision.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;
+        }
+        SourceIdentifier other = (SourceIdentifier) obj;
+        if (name == null) {
+            if (other.name != null) {
+                return false;
+            }
+        } else if (!name.equals(other.name)) {
+            return false;
+        }
+        if (revision == null) {
+            if (other.revision != null) {
+                return false;
+            }
+        } else if (!revision.equals(other.revision)) {
+            return false;
+        }
+        return true;
+    }
+
+    public static SourceIdentifier create(final String moduleName, final Optional<String> revision) {
+        return new SourceIdentifier(moduleName, revision);
+    }
+
+    /**
+     * Returns filename for this YANG module as specified in RFC 6020.
+     *
+     * Returns filename in format
+     * <code>name ['@' revision] '.yang'</code>
+     * <p>
+     * Where revision is  date in format YYYY-mm-dd.
+     * <p>
+     *
+     * @see http://tools.ietf.org/html/rfc6020#section-5.2
+     *
+     * @return Filename for this source identifier.
+     */
+    public String toYangFilename() {
+        return toYangFileName(name, Optional.fromNullable(revision));
+    }
+
+    @Override
+    public String toString() {
+        return "SourceIdentifier [name=" + name + "@" + revision + "]";
+    }
+
+    /**
+     * Returns filename for this YANG module as specified in RFC 6020.
+     *
+     * Returns filename in format
+     * <code>moduleName ['@' revision] '.yang'</code>
+     *
+     * Where Where revision-date is in format YYYY-mm-dd.
+     *
+     * <p>
+     * See
+     * http://tools.ietf.org/html/rfc6020#section-5.2
+     *
+     * @return Filename for this source identifier.
+     */
+    public static final String toYangFileName(final String moduleName, final Optional<String> revision) {
+        StringBuilder filename = new StringBuilder(moduleName);
+        if (revision.isPresent()) {
+            filename.append('@');
+            filename.append(revision.get());
+        }
+        filename.append(".yang");
+        return filename.toString();
+    }
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java
new file mode 100644 (file)
index 0000000..be52a90
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteSource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * YANG text schema source representation. Exposes an RFC6020 text representation
+ * as an {@link InputStream}.
+ */
+public abstract class YangTextSchemaSource extends ByteSource implements SchemaSourceRepresentation {
+    private final SourceIdentifier identifier;
+
+    protected YangTextSchemaSource(final SourceIdentifier identifier) {
+        this.identifier = Preconditions.checkNotNull(identifier);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final SourceIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Class<? extends YangTextSchemaSource> getType() {
+        return YangTextSchemaSource.class;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(Objects.toStringHelper(this).add("identifier", identifier)).toString();
+    }
+
+    /**
+     * Add subclass-specific attributes to the output {@link #toString()} output. Since
+     * subclasses are prevented from overriding {@link #toString()} for consistency
+     * reasons, they can add their specific attributes to the resulting string by attaching
+     * attributes to the supplied {@link ToStringHelper}.
+     *
+     * @param toStringHelper ToStringHelper onto the attributes can be added
+     * @return ToStringHelper supplied as input argument.
+     */
+    protected abstract ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper);
+
+    /**
+     * Create a new YangTextSchemaSource with a specific source identifier and backed
+     * by ByteSource, which provides the actual InputStreams.
+     *
+     * @param identifier SourceIdentifier of the resulting schema source
+     * @param delegate Backing ByteSource instance
+     * @return A new YangTextSchemaSource
+     */
+    public static YangTextSchemaSource delegateForByteSource(final SourceIdentifier identifier, final ByteSource delegate) {
+        return new DelegatedYangTextSchemaSource(identifier, delegate);
+    }
+
+    private static final class DelegatedYangTextSchemaSource extends YangTextSchemaSource implements Delegator<ByteSource> {
+        private final ByteSource delegate;
+
+        private DelegatedYangTextSchemaSource(final SourceIdentifier identifier, final ByteSource delegate) {
+            super(identifier);
+            this.delegate = Preconditions.checkNotNull(delegate);
+        }
+
+        @Override
+        public final ByteSource getDelegate() {
+            return delegate;
+        }
+
+        @Override
+        public InputStream openStream() throws IOException {
+            return delegate.openStream();
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return toStringHelper.add("delegate", delegate);
+        }
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java
new file mode 100644 (file)
index 0000000..b67aae6
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import org.w3c.dom.Document;
+
+/**
+ * Yin schema source representation. Exposes an RFC6020 YIN XML representation
+ * as an W3C {@link Document}.
+ */
+public interface YinSchemaSource extends SchemaSourceRepresentation {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    SourceIdentifier getIdentifier();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Class<? extends YinSchemaSource> getType();
+
+    /**
+     * Return schema source as a Yin-compliant Document.
+     *
+     * @return W3C DOM document.
+     */
+    Document getYinDocument();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java
new file mode 100644 (file)
index 0000000..a0a141b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Schema source provider implementations take care of resolving a {@link SourceIdentifier}
+ * into a particular representation of the schema source. Examples of resolution include
+ * fetching the source from an external source, opening a classpath resource, or similar.
+ *
+ * @param <T> Schema source representation type provided by this implementation
+ */
+@Beta
+public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
+    /**
+     * Returns a representation a for supplied YANG source identifier. The resolution
+     * criteria are as follows:
+     *
+     * <ul>
+     * <li> If the source identifier specifies a revision, this method returns either
+     * a representation of that particular revision, or report the identifier as absent
+     * by returning {@link Optional#absent()}.
+     * <li> If the source identifier does not specify a revision, this method returns
+     * the newest available revision, or {@link Optional#absent()}.
+     *
+     * In either case the returned representation is required to report a non-null
+     * revision in the {@link SourceIdentifier} returned from
+     * {@link SchemaSourceRepresentation#getIdentifier()}.
+     *
+     * Implementations are not required to provide constant behavior in time, notably
+     * this different invocation of this method may produce different results.
+     *
+     * @param sourceIdentifier source identifier
+     * @return source representation if supplied YANG module is available
+     *         {@link Optional#absent()} otherwise.
+     */
+    ListenableFuture<Optional<T>> getSource(SourceIdentifier sourceIdentifier);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java
new file mode 100644 (file)
index 0000000..6cdb6a3
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+public interface SchemaSourceRegistration extends Registration<SourceIdentifier> {
+    @Override
+    void close();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java
new file mode 100644 (file)
index 0000000..f9acf3d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Registry of all potentially available schema sources. Processes capable of
+ * dynamic schema discovery, such as OSGi registry scanners, NETCONF clients
+ * (with NETCONF monitoring extension) and similar can register
+ * {@link SchemaSourceProvider} instances which would then acquire the schema
+ * source.
+ */
+public interface SchemaSourceRegistry {
+    /**
+     * Register a new schema source which is potentially available from a provider.
+     * A registration does not guarantee that a subsequent call to
+     * {@link SchemaSourceProvider#getSource(SourceIdentifier)} will succeed.
+     *
+     * @param identifier Schema source identifier
+     * @param provider Resolver which can potentially resolve the identifier
+     * @param representation Schema source representation which the source may
+     *                       be available.
+     * @return A registration handle. Invoking {@link SchemaSourceRegistration#close()}
+     *         will cancel the registration.
+     */
+    <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
+            SourceIdentifier identifier, SchemaSourceProvider<? super T> provider, Class<T> representation);
+
+    /**
+     * Register a schema transformer. The registry can invoke it to transform between
+     * the various schema source formats.
+     *
+     * @param transformer Schema source transformer
+     * @return A registration handle. Invoking {@link SchemaTransformerRegistration#close()}
+     *         will cancel the registration.
+     */
+    SchemaTransformerRegistration registerSchemaSourceTransformer(SchemaSourceTransformer<?, ?> transformer);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java
new file mode 100644 (file)
index 0000000..b5c08de
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+/**
+ * Exception thrown when a failure to translate a schema source between
+ * representations.
+ */
+public class SchemaSourceTransformationException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public SchemaSourceTransformationException(final String message) {
+        super(message);
+    }
+
+    public SchemaSourceTransformationException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java
new file mode 100644 (file)
index 0000000..13d309e
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * An schema source representation transformation service. An instance can create
+ * some output schema source representation based on some input source representation.
+ *
+ * @param <I> Input {@link SchemaSourceRepresentation}
+ * @param <O> Output {@link SchemaSourceRepresentation}
+ */
+public interface SchemaSourceTransformer<I extends SchemaSourceRepresentation, O extends SchemaSourceRepresentation> {
+    /**
+     * Return the {@link SchemaSourceRepresentation} which this transformer
+     * accepts on its input.
+     *
+     * @return The input source representation type.
+     */
+    Class<I> getInputRepresentation();
+
+    /**
+     * Return the {@link SchemeSourceRepresentation} which this transformer
+     * produces on its output.
+     *
+     * @return The output source representation type.
+     */
+    Class<O> getOutputRepresentation();
+
+    /**
+     * Transform a schema source representation from its input form to
+     * the transformers output form.
+     *
+     * @param source Schema source in its source representation
+     * @return A future which produces the output schema source representation.
+     */
+    CheckedFuture<O, SchemaSourceTransformationException> transformSchemaSource(I source);
+
+    /**
+     * Return the relative cost of performing the transformation. When in doubt,
+     * return 1.
+     *
+     * @return Relative cost.
+     */
+    int getCost();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java
new file mode 100644 (file)
index 0000000..e64deee
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+
+public interface SchemaTransformerRegistration extends ObjectRegistration<SchemaSourceTransformer<?, ?>> {
+    @Override
+    void close();
+}
index 259a48e471b47005bce8dbb8d7bb9e1871956542..30e28d46bde7c04ae1b2e1be768fb34a6f67f2b7 100644 (file)
@@ -1,9 +1,18 @@
+/*
+ * 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.parser.builder.util;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+
 import java.util.Map;
 import java.util.Set;
+
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -46,12 +55,8 @@ public abstract class AbstractDocumentedDataNodeContainer extends AbstractDocume
 
     @Override
     public final DataSchemaNode getDataChildByName(final QName name) {
-        for (DataSchemaNode node : childNodes.values()) {
-            if (node.getQName().equals(name)) {
-                return node;
-            }
-        }
-        return null;
+        // Child nodes are keyed by their container name, so we can do a direct lookup
+        return childNodes.get(name);
     }
 
     @Override