BUG-997: Evolve the SchemaRegistry concepts 81/9581/9
authorRobert Varga <rovarga@cisco.com>
Fri, 1 Aug 2014 14:19:29 +0000 (16:19 +0200)
committerRobert Varga <rovarga@cisco.com>
Sun, 3 Aug 2014 10:47:20 +0000 (12:47 +0200)
This is the next step in SchemaRegistry evolution. Introduces
SchemaSourceListener and abstract classes for cache and transformer
patterns. Also completes implementation of the the abstract schema
registry and converts URLSchemaContextResolver. Finally it marks the
APIs as Beta, since they are being proven until we certify NETCONF
completely.

Change-Id: If82522c9a9a1913c818146da1247c8c5ca79b9c5
Signed-off-by: Robert Varga <rovarga@cisco.com>
35 files changed:
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java [deleted file]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java with 53% similarity]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java with 58% similarity]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java [deleted file]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java [deleted file]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java

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
deleted file mode 100644 (file)
index e101d9d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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/MissingSchemaSourceException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java
new file mode 100644 (file)
index 0000000..17c9c90
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.annotations.Beta;
+
+/**
+ * Exception thrown when a the specified schema source is not available.
+ */
+@Beta
+public class MissingSchemaSourceException extends SchemaSourceException {
+    private static final long serialVersionUID = 1L;
+
+    public MissingSchemaSourceException(final String message) {
+        super(message);
+    }
+
+    public MissingSchemaSourceException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
index 8869c9eb3ddb11a9d17732ba33bb0e0056e01986..2597e9c185481d8886d4ee65155e74d9264cdc37 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.CheckedFuture;
 
 import java.util.Collection;
@@ -20,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * based on a specification of what {@link SourceIdentifier}s are required
  * and dynamic recursive resolution.
  */
+@Beta
 public interface SchemaContextFactory {
     /**
      * Create a new schema context containing specified sources, pulling in
index 807bbf05de62bcf4ae7d3863fa47a6e843e214d8..73569d384d3cd76091466ebe5474c70a96b57e6c 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.CheckedFuture;
+
 import javax.annotation.Nonnull;
 
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -15,6 +18,7 @@ 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.
  */
+@Beta
 public interface SchemaRepository {
     /**
      * Instantiate a new {@link SchemaContextFactory}, which will filter available schema
@@ -26,4 +30,6 @@ public interface SchemaRepository {
      * @return A new schema context factory.
      */
     SchemaContextFactory createSchemaContextFactory(@Nonnull SchemaSourceFilter filter);
+
+    <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(@Nonnull SourceIdentifier id, @Nonnull Class<T> represetation);
 }
index b47e366a79755273b37be9e2a1a25335d3c6123a..e0be4adb7936c6afab7cd2be00e69440b6f4af0d 100644 (file)
@@ -7,38 +7,48 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
-import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.annotation.Nonnull;
 
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+
 /**
  * Exception thrown when a Schema Source fails to resolve.
  */
-public class SchemaResolutionException extends Exception {
+@Beta
+public class SchemaResolutionException extends SchemaSourceException {
     private static final long serialVersionUID = 1L;
-    private final Map<SourceIdentifier, Throwable> unresolvedSources;
+    private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+    private final Collection<SourceIdentifier> resolvedSources;
 
     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());
+        this(message, cause, Collections.<SourceIdentifier>emptySet(), ImmutableMultimap.<SourceIdentifier, ModuleImport>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 Collection<SourceIdentifier> resolvedSources,
+            final @Nonnull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        this(message, null, Collections.<SourceIdentifier>emptySet(), unsatisfiedImports);
     }
 
-    public SchemaResolutionException(final @Nonnull String message, final Throwable cause, @Nonnull final Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+    public SchemaResolutionException(final @Nonnull String message, final Throwable cause,
+            @Nonnull final Collection<SourceIdentifier> resolvedSources,
+            @Nonnull final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
         super(message, cause);
-        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+        this.unsatisfiedImports = ImmutableMultimap.copyOf(unsatisfiedImports);
+        this.resolvedSources = ImmutableList.copyOf(resolvedSources);
     }
 
     /**
@@ -47,13 +57,19 @@ public class SchemaResolutionException extends Exception {
      *
      * @return Source/reason map.
      */
-    public final Map<SourceIdentifier, Throwable> getUnresolvedSources() {
-        return unresolvedSources;
+    public final Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+
+
+    // FIXME: should be leak actual mapping?
+    public final Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
     }
 
     @Override
     public final String toString() {
-        return addToStringAttributes(Objects.toStringHelper(this).add("unresolvedSources", unresolvedSources)).toString();
+        return addToStringAttributes(Objects.toStringHelper(this).add("unsatisfiedImports", unsatisfiedImports)).toString();
     }
 
     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
@@ -5,20 +5,22 @@
  * 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;
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
 
 /**
- * Exception thrown when a failure to translate a schema source between
- * representations.
+ * Exception thrown when a failure to acquire a schema source occurs.
  */
-public class SchemaSourceTransformationException extends Exception {
+@Beta
+public class SchemaSourceException extends Exception {
     private static final long serialVersionUID = 1L;
 
-    public SchemaSourceTransformationException(final String message) {
+    public SchemaSourceException(final String message) {
         super(message);
     }
 
-    public SchemaSourceTransformationException(final String message, final Throwable cause) {
+    public SchemaSourceException(final String message, final Throwable cause) {
         super(message, cause);
     }
 }
index 188f83c0c592c47b6134bf080d4ed0a99924dfce..f881900754a53642d3696bfab3e67e2a47934ac2 100644 (file)
@@ -7,9 +7,55 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.Collections;
+
+/*
+ * A filter of schema sources. This is used to restrict which sources representation
+ * instances are allowed to participate in construction of a schema context. This
+ * allows, for example, to create an non-shared island, or require the sources to
+ * be certified before use.
+ */
+@Beta
 public interface SchemaSourceFilter {
+    /**
+     * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
+     */
+    public static final SchemaSourceFilter ALWAYS_ACCEPT = new SchemaSourceFilter() {
+        private final Iterable<Class<? extends SchemaSourceRepresentation>> REPRESENTATIONS =
+                Collections.<Class<? extends SchemaSourceRepresentation>>singletonList(SchemaSourceRepresentation.class);
+
+        @Override
+        public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
+            return REPRESENTATIONS;
+        }
+
+        @Override
+        public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
+            return Futures.immediateFuture(Boolean.TRUE);
+        }
+    };
+
+    /**
+     * Get the representations this filter supports. A schema source is translated
+     * into one of these representations before it is presented for filtering.
+     *
+     * @return Set of supported representations.
+     */
     Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations();
+
+    /**
+     * Check if a particular schema source is acceptable to the filter. The process
+     * of checking may be asynchronous, but at some point it needs to produce an
+     * affirmative or negative answer before the schema context construction can
+     * proceed.
+     *
+     * @param schemaSource Schema source to be filtered
+     * @return Promise of a filtering decision. The result should be {@link Boolean#TRUE}
+     *         if the source is acceptable.
+     */
     ListenableFuture<Boolean> apply(SchemaSourceRepresentation schemaSource);
 }
index b1261a9cd48a5883c86b955b12c3f187315a5cf8..90fc5ae740c2bf5cf2d07f0328681d1e1f987f07 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.concepts.Immutable;
 
@@ -35,6 +37,7 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * Implementations of this interface expected to comply with the {@link Immutable}
  * contract.
  */
+@Beta
 public interface SchemaSourceRepresentation extends Identifiable<SourceIdentifier>, Immutable {
     /**
      * {@inheritDoc}
index cd3a0fbefcaaed7198980ee545f7fe510654ac0b..58b011c36811cd60c3f9c0af9a1a7f93d9990988 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
@@ -14,7 +15,6 @@ 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),
@@ -34,9 +34,8 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * <p>
  * (For further reference see: http://tools.ietf.org/html/rfc6020#section-5.2 and
  * http://tools.ietf.org/html/rfc6022#section-3.1 ).
- *
- *
  */
+@Beta
 public final class SourceIdentifier implements Identifier, Immutable {
     private static final long serialVersionUID = 1L;
     private final String revision;
index be52a90f777131055f9423d98d9893d0568c3371..50ee12ef4d03358729456081c76216a020f5db71 100644 (file)
@@ -7,8 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.io.ByteSource;
 
@@ -21,6 +25,7 @@ import org.opendaylight.yangtools.concepts.Delegator;
  * YANG text schema source representation. Exposes an RFC6020 text representation
  * as an {@link InputStream}.
  */
+@Beta
 public abstract class YangTextSchemaSource extends ByteSource implements SchemaSourceRepresentation {
     private final SourceIdentifier identifier;
 
@@ -28,6 +33,12 @@ public abstract class YangTextSchemaSource extends ByteSource implements SchemaS
         this.identifier = Preconditions.checkNotNull(identifier);
     }
 
+    public static SourceIdentifier identifierFromFilename(final String name) {
+        checkArgument(name.endsWith(".yang"), "Filename %s does not have a .yang extension", name);
+        // FIXME: add revision-awareness
+        return SourceIdentifier.create(name.substring(0, name.length() - 5), Optional.<String>absent());
+    }
+
     /**
      * {@inheritDoc}
      */
index b67aae628914c20e7b5fe2779152abb63814c2a1..f09f525533e62982f6610c96344501414bcfa8b4 100644 (file)
@@ -7,12 +7,15 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+
 import org.w3c.dom.Document;
 
 /**
  * Yin schema source representation. Exposes an RFC6020 YIN XML representation
  * as an W3C {@link Document}.
  */
+@Beta
 public interface YinSchemaSource extends SchemaSourceRepresentation {
     /**
      * {@inheritDoc}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java
new file mode 100644 (file)
index 0000000..fae4467
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * A potential schema source. Instances of this class track the various
+ * representations of a schema source and the cost attached to obtaining
+ * the source from them.
+ */
+@Beta
+public final class PotentialSchemaSource<T extends SchemaSourceRepresentation> {
+    /**
+     * Each registered source has a cost associated with it. Since a particular
+     * representation can be acquired by various means, here are general constants
+     * for common cases.
+     */
+    public enum Costs {
+        /**
+         * The source is immediately available, via a lookup or similar.
+         */
+        IMMEDIATE(0),
+        /**
+         * The source is available via a computation. For transformation-type
+         * computation, the cost of acquiring the cost needs to be added, too.
+         */
+        COMPUTATION(1),
+        /**
+         * The source is available by performing local IO, such that reading
+         * from a disk.
+         */
+        LOCAL_IO(4),
+        /**
+         * The source is available by performing remote IO, such as fetching
+         * from an HTTP server or similar.
+         */
+        REMOTE_IO(8);
+
+        private final int value;
+
+        private Costs(final int value) {
+            this.value = value;
+        }
+
+        /**
+         * The the cost value.
+         *
+         * @return Const constant.
+         */
+        public int getValue() {
+            return value;
+        }
+    }
+
+    private final Class<? extends T> representation;
+    private final SourceIdentifier sourceIdentifier;
+    private final int cost;
+
+    private PotentialSchemaSource(final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        this.representation = Preconditions.checkNotNull(representation);
+        this.sourceIdentifier = Preconditions.checkNotNull(sourceIdentifier);
+        Preconditions.checkArgument(cost >= 0, "cost has to be non-negative");
+        this.cost = cost;
+    }
+
+    public static final <T extends SchemaSourceRepresentation> PotentialSchemaSource<T> create(final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        return new PotentialSchemaSource<>(sourceIdentifier, representation, cost);
+    }
+
+    public SourceIdentifier getSourceIdentifier() {
+        return sourceIdentifier;
+    }
+
+    public Class<? extends T> getRepresentation() {
+        return representation;
+    }
+
+    public int getCost() {
+        return cost;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + cost;
+        result = prime * result + representation.hashCode();
+        result = prime * result + sourceIdentifier.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PotentialSchemaSource)) {
+            return false;
+        }
+        final PotentialSchemaSource<?> other = (PotentialSchemaSource<?>) obj;
+        if (cost != other.cost) {
+            return false;
+        }
+        if (!representation.equals(other.representation)) {
+            return false;
+        }
+        if (!sourceIdentifier.equals(other.sourceIdentifier)) {
+            return false;
+        }
+        return true;
+    }
+}
@@ -7,9 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import com.google.common.annotations.Beta;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Registration of a SchemaSourceListener.
+ */
+@Beta
+public interface SchemaListenerRegistration extends ListenerRegistration<SchemaSourceListener> {
 
-public interface SchemaTransformerRegistration extends ObjectRegistration<SchemaSourceTransformer<?, ?>> {
-    @Override
-    void close();
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java
new file mode 100644 (file)
index 0000000..b4f55a8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.EventListener;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * Listener for schema source lifecycle events.
+ */
+@Beta
+public interface SchemaSourceListener extends EventListener {
+    /**
+     * Invoked when the registry sees a concrete source. This callback is typically
+     * used by cache-type listeners, who intercept the source, store it locally and
+     * announce themselves as a provider of that particular schema source.
+     *
+     * @param source Schema source
+     */
+    void schemaSourceEncountered(SchemaSourceRepresentation source);
+
+    /**
+     * Invoked when a new schema source is registered by a provider. This call
+     * callback, along with {@link #schemaSourceUnregistered(PotentialSchemaSource)}
+     * is typically used by transformer-type listeners, who intercept the registration
+     * if the advertised representation matches their input type and register
+     * themselves as a potential provider of the same source in their output
+     * representation type.
+     *
+     * @param sources Newly available sources
+     */
+    void schemaSourceRegistered(Iterable<PotentialSchemaSource<?>> sources);
+
+    /**
+     * Invoked when a schema source is unregistered.
+     *
+     * @param source Schema source representation
+     */
+    void schemaSourceUnregistered(PotentialSchemaSource<?> source);
+}
index a0a141bd3759f4f1a4dac00e3f3cb2706d1114bd..70ad6813e8286f4c22025b312395af34ebbc6418 100644 (file)
@@ -8,9 +8,10 @@
 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 com.google.common.util.concurrent.CheckedFuture;
 
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -29,10 +30,9 @@ public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
      *
      * <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()}.
+     * a representation of that particular revision or throw {@link MissingSchemaSourceException}.
      * <li> If the source identifier does not specify a revision, this method returns
-     * the newest available revision, or {@link Optional#absent()}.
+     * the newest available revision, or throws {@link MissingSchemaSourceException}.
      *
      * In either case the returned representation is required to report a non-null
      * revision in the {@link SourceIdentifier} returned from
@@ -43,7 +43,7 @@ public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
      *
      * @param sourceIdentifier source identifier
      * @return source representation if supplied YANG module is available
-     *         {@link Optional#absent()} otherwise.
+     *
      */
-    ListenableFuture<Optional<T>> getSource(SourceIdentifier sourceIdentifier);
+    CheckedFuture<? extends T, SchemaSourceException> getSource(SourceIdentifier sourceIdentifier);
 }
index 3c3c5fef7748dda7a9bdbe3ae507118a46c4f617..6937c53e98ecbc42544cf61fa7f00b0ee792e880 100644 (file)
@@ -7,10 +7,16 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 
-public interface SchemaSourceRegistration extends ObjectRegistration<SourceIdentifier> {
+/**
+ * Registration of a schema source.
+ */
+@Beta
+public interface SchemaSourceRegistration<T extends SchemaSourceRepresentation> extends ObjectRegistration<PotentialSchemaSource<T>> {
     @Override
     void close();
 }
index f9acf3d456cb260f4f924356c08caad549e4b128..075fcb5382d44ffb34532d20685c92de4cf86c7a 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -17,29 +19,28 @@ import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
  * {@link SchemaSourceProvider} instances which would then acquire the schema
  * source.
  */
+@Beta
 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.
+     * @param source Schema source details
      * @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);
+    <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(SchemaSourceProvider<? super T> provider, PotentialSchemaSource<T> source);
 
     /**
-     * Register a schema transformer. The registry can invoke it to transform between
-     * the various schema source formats.
+     * Register a schema source listener. The listener will be notified as new
+     * sources and their representations become available, subject to the provided
+     * filter.
      *
-     * @param transformer Schema source transformer
-     * @return A registration handle. Invoking {@link SchemaTransformerRegistration#close()}
+     * @param listener Schema source listener
+     * @return A registration handle. Invoking {@link SchemaListenerRegistration#close()}
      *         will cancel the registration.
      */
-    SchemaTransformerRegistration registerSchemaSourceTransformer(SchemaSourceTransformer<?, ?> transformer);
+    SchemaListenerRegistration registerSchemaSourceListener(SchemaSourceListener listener);
 }
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
deleted file mode 100644 (file)
index 13d309e..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java
new file mode 100644 (file)
index 0000000..e720f8d
--- /dev/null
@@ -0,0 +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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+
+public abstract class AbstractSchemaListenerRegistration extends AbstractListenerRegistration<SchemaSourceListener> implements SchemaListenerRegistration {
+    protected AbstractSchemaListenerRegistration(final SchemaSourceListener listener) {
+        super(listener);
+    }
+}
index 4dd30b90a99c3470c92cbd1082ed2793598d4bd1..53563fde69cea887b7d0a90e5be70350f58e8925 100644 (file)
  */
 package org.opendaylight.yangtools.yang.model.repo.util;
 
-import com.google.common.base.Optional;
+import com.google.common.annotations.Beta;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
-import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureFallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
+/**
+ * Abstract base class for {@link SchemaRepository} implementations. It handles registration
+ * and lookup of schema sources, subclasses need only to provide their own
+ * {@link #createSchemaContextFactory(SchemaSourceFilter)} implementation.
+ */
+@Beta
+public abstract class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class);
-    private static final Comparator<SchemaTransformerRegistration> TRANSFORMER_COST_COMPARATOR = new Comparator<SchemaTransformerRegistration>() {
-        @Override
-        public int compare(final SchemaTransformerRegistration o1, final SchemaTransformerRegistration o2) {
-            return o1.getInstance().getCost() - o2.getInstance().getCost();
-        }
-    };
+    private static final ExceptionMapper<SchemaSourceException> FETCH_MAPPER = ReflectiveExceptionMapper.create("Schema source fetch", SchemaSourceException.class);
 
     /*
-     * Output-type -> transformer map. Our usage involves knowing the destination type,
-     * so we have to work backwards and find a transformer chain which will get us
-     * to that representation given our available sources.
+     * Source identifier -> representation -> provider map. We usually are looking for
+     * a specific representation of a source.
      */
-    private final Multimap<Class<? extends SchemaSourceRepresentation>, SchemaTransformerRegistration> transformers =
-            HashMultimap.create();
+    @GuardedBy("this")
+    private final Map<SourceIdentifier, Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>>> sources = new HashMap<>();
 
     /*
-     * Source identifier -> representation -> provider map. We usually are looking for
-     * a specific representation a source.
+     * Schema source listeners.
      */
-    private final Map<SourceIdentifier, Multimap<Class<?>, AbstractSchemaSourceRegistration>> sources = new HashMap<>();
+    @GuardedBy("this")
+    private final Collection<SchemaListenerRegistration> listeners = new ArrayList<>();
 
+    private static final <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
+        final AbstractSchemaSourceRegistration<?> reg = it.next();
 
-    private static final <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration> it) {
-        if (!it.hasNext()) {
-            return Futures.immediateFuture(Optional.<T>absent());
-        }
-
-        return Futures.transform(((SchemaSourceProvider<T>)it.next().getProvider()).getSource(id), new AsyncFunction<Optional<T>, Optional<T>>() {
+        @SuppressWarnings("unchecked")
+        final CheckedFuture<? extends T, SchemaSourceException> f = ((SchemaSourceProvider<T>)reg.getProvider()).getSource(id);
+        return Futures.makeChecked(Futures.withFallback(f, new FutureFallback<T>() {
             @Override
-            public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
-                if (input.isPresent()) {
-                    return Futures.immediateFuture(input);
-                } else {
+            public ListenableFuture<T> create(final Throwable t) throws SchemaSourceException {
+                LOG.debug("Failed to acquire source from {}", reg, t);
+
+                if (it.hasNext()) {
                     return fetchSource(id, it);
                 }
-            }
-        });
-    }
-
-    private <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> transformSchemaSource(final SourceIdentifier id, final Class<T> representation) {
-        final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
-        if (srcs.isEmpty()) {
-            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
-                    String.format("No providers producing a representation of %s registered", id)));
-        }
-
-        final Collection<SchemaTransformerRegistration> ts = transformers.get(representation);
-        if (ts.isEmpty()) {
-            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
-                    String.format("No transformers producing representation %s registered", representation)));
-        }
 
-        // Build up the candidate list
-        final List<SchemaTransformerRegistration> candidates = new ArrayList<>();
-        for (SchemaTransformerRegistration tr : ts) {
-            final SchemaSourceTransformer<?, ?> t = tr.getInstance();
-            final Class<?> i = t.getInputRepresentation();
-            if (srcs.containsKey(i)) {
-                candidates.add(tr);
-            } else {
-                LOG.debug("Provider for {} in {} not found, skipping transfomer {}", id, i, t);
+                throw new MissingSchemaSourceException("All available providers exhausted");
             }
-        }
-
-        if (candidates.isEmpty()) {
-            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
-                    String.format("No matching source/transformer pair for source %s representation %s found", id, representation)));
-        }
-
-        Collections.sort(candidates, TRANSFORMER_COST_COMPARATOR);
-        // return transform(candidates.iterator(), id);
-        return null;
+        }), FETCH_MAPPER);
     }
 
-    /**
-     * Obtain a SchemaSource is selected representation
-     */
-    protected <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
-        final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
+    @Override
+    public <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
+        final Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> srcs = sources.get(id);
         if (srcs == null) {
-            LOG.debug("No providers registered for source {}", id);
-            return Futures.immediateFuture(Optional.<T>absent());
+            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("No providers registered for source" + id));
         }
 
-        final Collection<AbstractSchemaSourceRegistration> candidates = srcs.get(representation);
-        return Futures.transform(AbstractSchemaRepository.<T>fetchSource(id, candidates.iterator()), new AsyncFunction<Optional<T>, Optional<T>>() {
-            @Override
-            public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
-                if (input.isPresent()) {
-                    return Futures.immediateFuture(input);
-                }
-
-                return transformSchemaSource(id, representation);
-            }
-        });
-    }
+        final Iterator<AbstractSchemaSourceRegistration<?>> regs = srcs.get(representation).iterator();
+        if (!regs.hasNext()) {
+            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(
+                    new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available"));
+        }
 
-    @Override
-    public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) {
-        // TODO Auto-generated method stub
-        return null;
+        return fetchSource(id, regs);
     }
 
-    private void addSource(final SourceIdentifier id, final Class<?> rep, final AbstractSchemaSourceRegistration reg) {
-        Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+    private synchronized <T extends SchemaSourceRepresentation> void addSource(final PotentialSchemaSource<T> source, final AbstractSchemaSourceRegistration<T> reg) {
+        Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
         if (m == null) {
             m = HashMultimap.create();
-            sources.put(id, m);
+            sources.put(source.getSourceIdentifier(), m);
         }
 
-        m.put(rep, reg);
+        m.put(source.getRepresentation(), reg);
+
+        final Collection<PotentialSchemaSource<?>> reps = Collections.<PotentialSchemaSource<?>>singleton(source);
+        for (SchemaListenerRegistration l : listeners) {
+            l.getInstance().schemaSourceRegistered(reps);
+        }
     }
 
-    private void removeSource(final SourceIdentifier id, final Class<?> rep, final SchemaSourceRegistration reg) {
-        final Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+    private synchronized <T extends SchemaSourceRepresentation> void removeSource(final PotentialSchemaSource<?> source, final SchemaSourceRegistration<?> reg) {
+        final Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
         if (m != null) {
-            m.remove(rep, reg);
+            m.remove(source.getRepresentation(), reg);
+
+            for (SchemaListenerRegistration l : listeners) {
+                l.getInstance().schemaSourceUnregistered(source);
+            }
+
             if (m.isEmpty()) {
                 sources.remove(m);
             }
@@ -163,29 +130,40 @@ public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceR
     }
 
     @Override
-    public <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
-            final SourceIdentifier identifier, final SchemaSourceProvider<? super T> provider, final Class<T> representation) {
-        final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(identifier, provider) {
+    public <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(final SchemaSourceProvider<? super T> provider, final PotentialSchemaSource<T> source) {
+        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(provider, source) {
             @Override
             protected void removeRegistration() {
-                removeSource(identifier, representation, this);
+                removeSource(source, this);
             }
         };
 
-        addSource(identifier, representation, ret);
+        addSource(source, ret);
         return ret;
     }
 
     @Override
-    public SchemaTransformerRegistration registerSchemaSourceTransformer(final SchemaSourceTransformer<?, ?> transformer) {
-        final SchemaTransformerRegistration ret = new AbstractSchemaTransformerRegistration(transformer) {
+    public SchemaListenerRegistration registerSchemaSourceListener(final SchemaSourceListener listener) {
+        final SchemaListenerRegistration ret = new AbstractSchemaListenerRegistration(listener) {
             @Override
             protected void removeRegistration() {
-                transformers.remove(transformer.getOutputRepresentation(), this);
+                listeners.remove(this);
             }
         };
 
-        transformers.put(transformer.getOutputRepresentation(), ret);
+        synchronized (this) {
+            final Collection<PotentialSchemaSource<?>> col = new ArrayList<>();
+            for (Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m : sources.values()) {
+                for (AbstractSchemaSourceRegistration<?> r : m.values()) {
+                    col.add(r.getInstance());
+                }
+            }
+
+            // Notify first, so translator-type listeners, who react by registering a source
+            // do not cause infinite loop.
+            listener.schemaSourceRegistered(col);
+            listeners.add(ret);
+        }
         return ret;
     }
 }
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java
new file mode 100644 (file)
index 0000000..498f403
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+/**
+ * Abstract base class for cache-type SchemaSourceListeners. It needs to be
+ * registered with a {@link SchemaSourceRegistry}, where it gets notifications
+ * from. It performs filtering and {@link #offer(SchemaSourceRepresentation)}s
+ * conforming sources to the subclass.
+ *
+ * @param <T> Cached schema source type.
+ */
+public abstract class AbstractSchemaSourceCache<T extends SchemaSourceRepresentation> implements SchemaSourceListener, SchemaSourceProvider<T> {
+    private final SchemaSourceRegistry consumer;
+    private final Class<T> representation;
+    private final Costs cost;
+
+    protected AbstractSchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final Costs cost) {
+        this.consumer = Preconditions.checkNotNull(consumer);
+        this.representation = Preconditions.checkNotNull(representation);
+        this.cost = Preconditions.checkNotNull(cost);
+    }
+
+    /**
+     * Offer a schema source in requested representation for caching. Subclasses
+     * need to implement this method to store the schema source. Once they have
+     * determined to cache the source, they should call {@link #register(SourceIdentifier)}.
+     *
+     * @param source schema source
+     */
+    protected abstract void offer(T source);
+
+    /**
+     * Register the presence of a cached schema source with the consumer. Subclasses
+     * need to call this method once they have cached a schema source representation,
+     * or when they have determined they have a schema source is available -- like
+     * when a persistent cache reads its cache index.
+     *
+     * @param sourceIdentifier Source identifier
+     * @return schema source registration, which the subclass needs to
+     *         {@link SchemaSourceRegistration#close() once it expunges the source
+     *         from the cache.
+     */
+    protected final SchemaSourceRegistration<T> register(final SourceIdentifier sourceIdentifier) {
+        final PotentialSchemaSource<T> src = PotentialSchemaSource.create(sourceIdentifier, representation, cost.getValue());
+        return consumer.registerSchemaSource(this, src);
+    }
+
+    @Override
+    public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        if (representation.isAssignableFrom(source.getType())) {
+            @SuppressWarnings("unchecked")
+            final T src = (T)source;
+            offer(src);
+        }
+    }
+
+    @Override
+    public final void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        // Not interesting
+    }
+
+    @Override
+    public final void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        // Not interesting
+    }
+}
index 9ed0afefc727eb1771d8055a7a661433cafd6128..241cc15996426321cf2f4051cd5a8c8a0f1e0444 100644 (file)
@@ -10,19 +10,20 @@ package org.opendaylight.yangtools.yang.model.repo.util;
 import com.google.common.base.Preconditions;
 
 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 
-public abstract class AbstractSchemaSourceRegistration extends AbstractObjectRegistration<SourceIdentifier> implements SchemaSourceRegistration {
+public abstract class AbstractSchemaSourceRegistration<T extends SchemaSourceRepresentation> extends AbstractObjectRegistration<PotentialSchemaSource<T>> implements SchemaSourceRegistration<T> {
     private final SchemaSourceProvider<?> provider;
 
-    protected AbstractSchemaSourceRegistration(final SourceIdentifier identifier, final SchemaSourceProvider<?> provider) {
-        super(identifier);
+    protected AbstractSchemaSourceRegistration(final SchemaSourceProvider<?> provider, final PotentialSchemaSource<T> source) {
+        super(source);
         this.provider = Preconditions.checkNotNull(provider);
     }
 
-    protected SchemaSourceProvider<?> getProvider() {
+    protected final SchemaSourceProvider<?> getProvider() {
         return provider;
     }
 }
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java
deleted file mode 100644 (file)
index d264ae0..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.util;
-
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration;
-
-public abstract class AbstractSchemaTransformerRegistration extends AbstractObjectRegistration<SchemaSourceTransformer<?, ?>> implements SchemaTransformerRegistration {
-    protected AbstractSchemaTransformerRegistration(
-            final SchemaSourceTransformer<?, ?> transformer) {
-        super(transformer);
-    }
-}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java
new file mode 100644 (file)
index 0000000..ac820d2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T> {
+    private static final class CacheEntry<T extends SchemaSourceRepresentation> {
+        private final SchemaSourceRegistration<T> reg;
+        private final T source;
+
+        public CacheEntry(final T source, final SchemaSourceRegistration<T> reg) {
+            this.source = Preconditions.checkNotNull(source);
+            this.reg = Preconditions.checkNotNull(reg);
+        }
+    }
+
+    private static final RemovalListener<SourceIdentifier, CacheEntry<?>> LISTENER = new RemovalListener<SourceIdentifier, CacheEntry<?>>() {
+        @Override
+        public void onRemoval(final RemovalNotification<SourceIdentifier, CacheEntry<?>> notification) {
+            notification.getValue().reg.close();
+        }
+    };
+
+    private final Cache<SourceIdentifier, CacheEntry<T>> cache;
+
+    protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final int maxSize) {
+        super(consumer, representation, Costs.IMMEDIATE);
+        cache = CacheBuilder.newBuilder().softValues().maximumSize(maxSize).removalListener(LISTENER).build();
+    }
+
+    @Override
+    public CheckedFuture<? extends T, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final CacheEntry<T> present = cache.getIfPresent(sourceIdentifier);
+        if (present != null) {
+            return Futures.immediateCheckedFuture(present.source);
+        }
+
+        return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found"));
+    }
+
+    @Override
+    protected void offer(final T source) {
+        final CacheEntry<T> present = cache.getIfPresent(source.getIdentifier());
+        if (present == null) {
+            final SchemaSourceRegistration<T> reg = register(source.getIdentifier());
+            cache.put(source.getIdentifier(), new CacheEntry<T>(source, reg));
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java
new file mode 100644 (file)
index 0000000..3cd59b2
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+
+final class RefcountedRegistration {
+    private final SchemaSourceRegistration<?> reg;
+    private int refcount = 1;
+
+    RefcountedRegistration(final SchemaSourceRegistration<?> reg) {
+        this.reg = Preconditions.checkNotNull(reg);
+    }
+
+    public void incRef() {
+        refcount++;
+    }
+
+    public boolean decRef() {
+        Preconditions.checkState(refcount > 0, "Refcount underflow: %s", refcount);
+
+        if (0 == --refcount) {
+            reg.close();
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java
new file mode 100644 (file)
index 0000000..7b58006
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+public class SchemaSourceTransformer<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation> implements SchemaSourceListener, SchemaSourceProvider<D> {
+    private static final ExceptionMapper<SchemaSourceException> MAPPER = ReflectiveExceptionMapper.create("Source transformation", SchemaSourceException.class);
+
+    public static interface Transformation<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation> extends AsyncFunction<S, D> {
+        @Override
+        CheckedFuture<D, SchemaSourceException> apply(final S input) throws Exception;
+    }
+
+    private final Map<PotentialSchemaSource<?>, RefcountedRegistration> sources = new HashMap<>();
+    private final SchemaSourceRegistry consumer;
+    private final SchemaRepository provider;
+    private final AsyncFunction<S, D> function;
+    private final Class<S> srcClass;
+    private final Class<D> dstClass;
+
+    public SchemaSourceTransformer(final SchemaRepository provider, final Class<S> srcClass,
+            final SchemaSourceRegistry consumer, final Class<D> dstClass, final AsyncFunction<S, D> function) {
+        this.provider = Preconditions.checkNotNull(provider);
+        this.consumer = Preconditions.checkNotNull(consumer);
+        this.function = Preconditions.checkNotNull(function);
+        this.srcClass = Preconditions.checkNotNull(srcClass);
+        this.dstClass = Preconditions.checkNotNull(dstClass);
+    }
+
+    @Override
+    public CheckedFuture<D, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final CheckedFuture<S, SchemaSourceException> f = provider.getSchemaSource(sourceIdentifier, srcClass);
+        return Futures.makeChecked(Futures.transform(f, function), MAPPER);
+    }
+
+    @Override
+    public final void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        // Not interesting
+    }
+
+    @Override
+    public final void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        for (PotentialSchemaSource<?> src : sources) {
+            final Class<?> rep = src.getRepresentation();
+            if (srcClass.isAssignableFrom(rep) && dstClass != rep) {
+                registerSource(src);
+            }
+        }
+    }
+
+    @Override
+    public final void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        final Class<?> rep = source.getRepresentation();
+        if (srcClass.isAssignableFrom(rep) && dstClass != rep) {
+            unregisterSource(source);
+        }
+    }
+
+    private void registerSource(final PotentialSchemaSource<?> src) {
+        RefcountedRegistration reg = sources.get(src);
+        if (reg != null) {
+            reg.incRef();
+            return;
+        }
+
+        final PotentialSchemaSource<D> newSrc = PotentialSchemaSource.create(src.getSourceIdentifier(), dstClass,
+                src.getCost() + PotentialSchemaSource.Costs.COMPUTATION.getValue());
+
+        final SchemaSourceRegistration<D> r = consumer.registerSchemaSource(this, newSrc);
+        sources.put(src, new RefcountedRegistration(r));
+    }
+
+    private void unregisterSource(final PotentialSchemaSource<?> src) {
+        final RefcountedRegistration reg = sources.get(src);
+        if (reg != null && reg.decRef()) {
+            sources.remove(src);
+        }
+    }
+}
index 50354f5ed8956fb29d03520f7affdfdeee54c2d7..45bedd8c3a9b7da64a762d74a141549adc13065a 100644 (file)
@@ -26,6 +26,7 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.st
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
+
 import java.net.URI;
 import java.text.DateFormat;
 import java.text.ParseException;
@@ -33,7 +34,9 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+
 import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext;
@@ -96,6 +99,7 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuil
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 public final class YangParserListenerImpl extends YangParserBaseListener {
     private static final Logger LOG = LoggerFactory.getLogger(YangParserListenerImpl.class);
     private static final Splitter COLON_SPLITTER = Splitter.on(':');
@@ -113,6 +117,24 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
         this.sourcePath = sourcePath;
     }
 
+    /**
+     * Create a new instance.
+     *
+     * FIXME: the resulting type needs to be extracted, such that we can reuse
+     *        the "BaseListener" aspect, which need not be exposed to the user.
+     *        Maybe factor out a base class into repo.spi?
+     *
+     * @param sourcePath
+     * @param walker
+     * @param tree
+     * @return
+     */
+    public static YangParserListenerImpl create(final String sourcePath, final ParseTreeWalker walker, final ParseTree tree) {
+        final YangParserListenerImpl ret = new YangParserListenerImpl(sourcePath);
+        walker.walk(ret, tree);
+        return ret;
+    }
+
     @Override
     public void enterModule_stmt(final YangParser.Module_stmtContext ctx) {
         moduleName = stringFromNode(ctx);
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java
new file mode 100644 (file)
index 0000000..8dee836
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.repo;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public abstract class AbstractURLRegistration extends AbstractObjectRegistration<YangTextSchemaSource> implements URLRegistration{
+    protected AbstractURLRegistration(final YangTextSchemaSource text) {
+        super(text);
+    }
+}
\ No newline at end of file
index 45ad366285af4c7f5244ebed4ff22c6b3429e336..5bc6616c869013f2d13cbb28406ba8df14922dba 100644 (file)
@@ -71,6 +71,8 @@ final class DependencyResolver {
         return rev == null && findWildcard(haystack, mi.getModuleName()) != null;
     }
 
+
+
     public static final DependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
         final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
         final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java
new file mode 100644 (file)
index 0000000..5873856
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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.repo;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class SharedSchemaContextFactory implements SchemaContextFactory {
+    private static final ExceptionMapper<SchemaResolutionException> MAPPER = ReflectiveExceptionMapper.create("resolve sources", SchemaResolutionException.class);
+    private static final Logger LOG = LoggerFactory.getLogger(SharedSchemaContextFactory.class);
+
+    private final Function<SourceIdentifier, ListenableFuture<ASTSchemaSource>> requestSources = new Function<SourceIdentifier, ListenableFuture<ASTSchemaSource>>() {
+        @Override
+        public ListenableFuture<ASTSchemaSource> apply(final SourceIdentifier input) {
+            return repository.getSchemaSource(input, ASTSchemaSource.class);
+        }
+    };
+    private final Cache<Collection<SourceIdentifier>, SchemaContext> cache = CacheBuilder.newBuilder().softValues().build();
+
+    private final AsyncFunction<List<ASTSchemaSource>, SchemaContext> assembleSources = new AsyncFunction<List<ASTSchemaSource>, SchemaContext>() {
+        @Override
+        public ListenableFuture<SchemaContext> apply(final List<ASTSchemaSource> sources) throws SchemaResolutionException {
+            final Map<SourceIdentifier, ASTSchemaSource> srcs =
+                    Maps.uniqueIndex(sources, ASTSchemaSource.GET_IDENTIFIER);
+            final Map<SourceIdentifier, YangModelDependencyInfo> deps =
+                    Maps.transformValues(srcs, ASTSchemaSource.GET_DEPINFO);
+
+            LOG.debug("Resolving dependency reactor {}", deps);
+
+            final DependencyResolver res = DependencyResolver.create(deps);
+            if (!res.getUnresolvedSources().isEmpty()) {
+                LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+
+                // FIXME: push into DependencyResolver
+
+                throw new SchemaResolutionException("Failed to resolve required models",
+                        res.getResolvedSources(), res.getUnsatisfiedImports());
+            }
+
+            final Map<SourceIdentifier, ParserRuleContext> asts =
+                    Maps.transformValues(srcs, ASTSchemaSource.GET_AST);
+
+            final ParseTreeWalker walker = new ParseTreeWalker();
+            final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
+
+            for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
+                ModuleBuilder moduleBuilder =
+                        YangParserListenerImpl.create(entry.getKey().getName(), walker, entry.getValue()).getModuleBuilder();
+
+                moduleBuilder.setSource(srcs.get(entry.getKey()).getYangText());
+                sourceToBuilder.put(entry.getKey(), moduleBuilder);
+            }
+            LOG.debug("Modules ready for integration");
+
+            final YangParserImpl parser = YangParserImpl.getInstance();
+            final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
+            LOG.debug("Integrated cross-references modules");
+            return Futures.immediateCheckedFuture(parser.assembleContext(modules));
+        }
+    };
+
+    private final SharedSchemaRepository repository;
+    // FIXME: ignored right now
+    private final SchemaSourceFilter filter;
+
+    public SharedSchemaContextFactory(final SharedSchemaRepository repository, final SchemaSourceFilter filter) {
+        this.repository = Preconditions.checkNotNull(repository);
+        this.filter = Preconditions.checkNotNull(filter);
+    }
+
+    @Override
+    public CheckedFuture<SchemaContext, SchemaResolutionException> createSchemaContext(final Collection<SourceIdentifier> requiredSources) {
+        final SchemaContext existing = cache.getIfPresent(requiredSources);
+        if (existing != null) {
+            LOG.debug("Returning cached context {}", existing);
+            return Futures.immediateCheckedFuture(existing);
+        }
+
+        // Request all sources be loaded
+        final ListenableFuture<List<ASTSchemaSource>> sf = Futures.allAsList(Collections2.transform(requiredSources, requestSources));
+
+        // Assemble sources into a schemacontext
+        final ListenableFuture<SchemaContext> cf = Futures.transform(sf, assembleSources);
+
+        // Populate cache when successful
+        Futures.addCallback(cf, new FutureCallback<SchemaContext>() {
+            @Override
+            public void onSuccess(final SchemaContext result) {
+                cache.put(requiredSources, result);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.info("Failed to assemble sources", t);
+            }
+        });
+
+        return Futures.makeChecked(cf, MAPPER);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java
new file mode 100644 (file)
index 0000000..1ff9f32
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.repo;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.util.AbstractSchemaRepository;
+
+/**
+ * A {@link SchemaRepository} which allows sharing of {@link SchemaContext} as
+ * long as their specification is the same.
+ *
+ * Note: for current implementation, "same" means the same filter and the same
+ * set of {@link SourceIdentifier}s.
+ */
+@Beta
+public final class SharedSchemaRepository extends AbstractSchemaRepository implements Identifiable<String> {
+    private final LoadingCache<SchemaSourceFilter, SchemaContextFactory> cache =
+            CacheBuilder.newBuilder().softValues().build(new CacheLoader<SchemaSourceFilter, SchemaContextFactory>() {
+                @Override
+                public SchemaContextFactory load(final SchemaSourceFilter key) {
+                    return new SharedSchemaContextFactory(SharedSchemaRepository.this, key);
+                }
+            });
+    private final String id;
+
+    public SharedSchemaRepository(final String id) {
+        this.id = Preconditions.checkNotNull(id);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) {
+        return cache.getUnchecked(filter);
+    }
+
+    @Override
+    public String toString() {
+        return "SchemaRepository: " + id;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java
new file mode 100644 (file)
index 0000000..d49ecc9
--- /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/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.repo;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public interface URLRegistration extends ObjectRegistration<YangTextSchemaSource> {
+    @Override
+    void close();
+}
\ No newline at end of file
index 8ee18e58a5a562d40bb65e0de26fef2b9ffebb58..47de6b1a540b5bd1b0a14014ce9a5364447977b3 100644 (file)
@@ -9,155 +9,78 @@ package org.opendaylight.yangtools.yang.parser.repo;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.common.base.Function;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Maps.EntryTransformer;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.tree.ParseTreeWalker;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
-import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
-import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@ThreadSafe
-public class URLSchemaContextResolver {
+@Beta
+public class URLSchemaContextResolver implements SchemaSourceProvider<YangTextSchemaSource> {
     private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
-    private static final Function<ASTSchemaSource, YangModelDependencyInfo> EXTRACT_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
-        @Override
-        public YangModelDependencyInfo apply(final ASTSchemaSource input) {
-            return input.getDependencyInformation();
-        }
-    };
-    private static final EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo> SQUASH_DEPINFO =
-            new EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo>() {
-        @Override
-        public YangModelDependencyInfo transformEntry(final SourceIdentifier key, final Collection<YangModelDependencyInfo> value) {
-            // FIXME: validate that all the info objects are the same
-            return value.iterator().next();
-        }
-    };
-    private static final Function<ASTSchemaSource, ParserRuleContext> EXTRACT_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
-        @Override
-        public ParserRuleContext apply(final ASTSchemaSource input) {
-            return input.getAST();
-        }
-    };
-    private static final EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext> SQUASH_AST =
-            new EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext>() {
-        @Override
-        public ParserRuleContext transformEntry(final SourceIdentifier key, final Collection<ParserRuleContext> value) {
-            // FIXME: validate that all the info objects are the same
-            return value.iterator().next();
-        }
-    };
-
-    @GuardedBy("this")
-    private final Multimap<SourceIdentifier, ASTSchemaSource> resolvedRegs = ArrayListMultimap.create();
-    private final AtomicReference<Optional<SchemaContext>> currentSchemaContext = new AtomicReference<>(Optional.<SchemaContext>absent());
-    private final Queue<URLRegistration> outstandingRegs = new ConcurrentLinkedQueue<>();
-    private final TextToASTTransformer transformer;
-    @GuardedBy("this")
-    private Object version = new Object();
-    @GuardedBy("this")
-    private Object contextVersion = version;
-
-    private final class URLRegistration extends AbstractObjectRegistration<URL> {
-        @GuardedBy("this")
-        private CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future;
-        @GuardedBy("this")
-        private ASTSchemaSource result;
-
-        protected URLRegistration(final URL url, final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future) {
-            super(url);
-            this.future = Preconditions.checkNotNull(future);
-        }
-
-        private synchronized boolean setResult(final ASTSchemaSource result) {
-            if (future != null) {
-                this.result = result;
-                return true;
-            } else {
-                return false;
-            }
-        }
 
-        @Override
-        protected void removeRegistration() {
-            // Cancel the future, but it may already be completing
-            future.cancel(false);
-
-            synchronized (this) {
-                future = null;
-                outstandingRegs.remove(this);
-                if (result != null) {
-                    removeSchemaSource(result);
-                }
-            }
-        }
-    }
-
-    private URLSchemaContextResolver(final TextToASTTransformer transformer) {
-        this.transformer = Preconditions.checkNotNull(transformer);
+    private final Cache<SourceIdentifier, YangTextSchemaSource> sources = CacheBuilder.newBuilder().build();
+    private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+    private final AtomicReference<Optional<SchemaContext>> currentSchemaContext =
+            new AtomicReference<>(Optional.<SchemaContext>absent());
+    private final SchemaSourceRegistry registry;
+    private final SchemaRepository repository;
+    private volatile Object version = new Object();
+    private volatile Object contextVersion = version;
+
+    private URLSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
+        this.repository = Preconditions.checkNotNull(repository);
+        this.registry = Preconditions.checkNotNull(registry);
     }
 
     public static URLSchemaContextResolver create(final String name) {
-        final ThreadFactory f = new ThreadFactoryBuilder().setDaemon(true).setNameFormat(name + "yangparser-%d").build();
-        final ListeningExecutorService s = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(f));
-
-        return new URLSchemaContextResolver(TextToASTTransformer.create(s));
+        final SharedSchemaRepository sharedRepo = new SharedSchemaRepository(name);
+        return new URLSchemaContextResolver(sharedRepo, sharedRepo);
     }
 
     /**
      * Register a URL hosting a YANG Text file.
      *
      * @param url URL
+     * @throws YangSyntaxErrorException When the YANG file is syntactically invalid
+     * @throws IOException when the URL is not readable
+     * @throws SchemaSourceException When parsing encounters general error
      */
-    public ObjectRegistration<URL> registerSource(final URL url) {
+    public URLRegistration registerSource(final URL url) throws SchemaSourceException, IOException, YangSyntaxErrorException {
         checkArgument(url != null, "Supplied URL must not be null");
 
-        final SourceIdentifier id = SourceIdentifier.create(url.getFile().toString(), Optional.<String>absent());
-        final YangTextSchemaSource text = new YangTextSchemaSource(id) {
+        final SourceIdentifier guessedId = new SourceIdentifier(url.getFile(), Optional.<String>absent());
+        final YangTextSchemaSource text = new YangTextSchemaSource(guessedId) {
             @Override
             public InputStream openStream() throws IOException {
                 return url.openStream();
@@ -169,39 +92,27 @@ public class URLSchemaContextResolver {
             }
         };
 
-        final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> ast = transformer.transformSchemaSource(text);
-        final URLRegistration reg = new URLRegistration(url, ast);
-        outstandingRegs.add(reg);
+        final ASTSchemaSource ast = TextToASTTransformer.TRANSFORMATION.apply(text).checkedGet();
+        LOG.trace("Resolved URL {} to source {}", url, ast);
 
-        Futures.addCallback(ast, new FutureCallback<ASTSchemaSource>() {
-            @Override
-            public void onSuccess(final ASTSchemaSource result) {
-                LOG.trace("Resolved URL {} to source {}", url, result);
+        final SourceIdentifier resolvedId = ast.getIdentifier();
+        final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+                PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0));
 
-                outstandingRegs.remove(reg);
-                if (reg.setResult(result)) {
-                    addSchemaSource(result);
-                }
-            }
+        requiredSources.add(resolvedId);
+        LOG.trace("Added source {} to schema context requirements", resolvedId);
+        version = new Object();
 
+        return new AbstractURLRegistration(text) {
             @Override
-            public void onFailure(final Throwable t) {
-                LOG.warn("Failed to parse YANG text from {}, ignoring it", url, t);
-                outstandingRegs.remove(reg);
+            protected void removeRegistration() {
+                requiredSources.remove(resolvedId);
+                LOG.trace("Removed source {} from schema context requirements", resolvedId);
+                version = new Object();
+                reg.close();
+                sources.invalidate(resolvedId);
             }
-        });
-
-        return reg;
-    }
-
-    private synchronized void addSchemaSource(final ASTSchemaSource src) {
-        resolvedRegs.put(src.getIdentifier(), src);
-        version = new Object();
-    }
-
-    private synchronized void removeSchemaSource(final ASTSchemaSource src) {
-        resolvedRegs.put(src.getIdentifier(), src);
-        version = new Object();
+        };
     }
 
     /**
@@ -210,64 +121,57 @@ public class URLSchemaContextResolver {
      *         new schema context was successfully built.
      */
     public Optional<SchemaContext> getSchemaContext() {
-        while (true) {
-            Optional<SchemaContext> result;
-            final Multimap<SourceIdentifier, ASTSchemaSource> sources;
-            final Object v;
-            synchronized (this) {
-                result = currentSchemaContext.get();
-                if (version == contextVersion) {
-                    return result;
+        final SchemaContextFactory factory = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+        Optional<SchemaContext> sc;
+        Object v;
+        do {
+            // Spin get stable context version
+            Object cv;
+            do {
+                cv = contextVersion;
+                sc = currentSchemaContext.get();
+                if (version == cv) {
+                    return sc;
                 }
+            } while (cv != contextVersion);
 
-                sources = ImmutableMultimap.copyOf(resolvedRegs);
+            // Version has been updated
+            Collection<SourceIdentifier> sources;
+            do {
                 v = version;
-            }
-
-            if (!sources.isEmpty()) {
-                final Map<SourceIdentifier, YangModelDependencyInfo> deps =
-                        Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_DEPINFO).asMap(), SQUASH_DEPINFO);
-
-                LOG.debug("Resolving dependency reactor {}", deps);
-                final DependencyResolver res = DependencyResolver.create(deps);
-                if (!res.getUnresolvedSources().isEmpty()) {
-                    LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+                sources = ImmutableList.copyOf(requiredSources);
+            } while (v != version);
+
+            while (true) {
+                final CheckedFuture<SchemaContext, SchemaResolutionException> f = factory.createSchemaContext(sources);
+                try {
+                    sc = Optional.of(f.checkedGet());
+                    break;
+                } catch (SchemaResolutionException e) {
+                    LOG.info("Failed to fully assemble schema context for {}", sources, e);
+                    sources = e.getResolvedSources();
                 }
-
-                final Map<SourceIdentifier, ParserRuleContext> asts =
-                        Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_AST).asMap(), SQUASH_AST);
-
-                final ParseTreeWalker walker = new ParseTreeWalker();
-                final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
-
-                for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
-                    final YangParserListenerImpl yangModelParser = new YangParserListenerImpl(entry.getKey().getName());
-                    walker.walk(yangModelParser, entry.getValue());
-                    ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
-
-                    moduleBuilder.setSource(sources.get(entry.getKey()).iterator().next().getYangText());
-                    sourceToBuilder.put(entry.getKey(), moduleBuilder);
-                }
-                LOG.debug("Modules ready for integration");
-
-                final YangParserImpl parser = YangParserImpl.getInstance();
-                final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
-                LOG.debug("Integrated cross-references modules");
-
-                result = Optional.of(parser.assembleContext(modules));
-            } else {
-                result = Optional.absent();
             }
 
             synchronized (this) {
-                if (v == version) {
-                    currentSchemaContext.set(result);
-                    contextVersion = version;
-                    return result;
+                if (contextVersion == cv) {
+                    currentSchemaContext.set(sc);
+                    contextVersion = v;
                 }
-
-                LOG.debug("Context version {} expected {}, retry", version, v);
             }
+        } while (version == v);
+
+        return sc;
+    }
+
+    @Override
+    public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier);
+        if (ret == null) {
+            return Futures.<YangTextSchemaSource, SchemaSourceException>immediateFailedCheckedFuture(
+                    new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered"));
         }
+
+        return Futures.immediateCheckedFuture(ret);
     }
 }
index ab353a04ad54c7b221e3bebbc194362d93e6cc6e..5ff245962e11d94856e82c40834863959186fc2a 100644 (file)
@@ -6,6 +6,8 @@
  */
 package org.opendaylight.yangtools.yang.parser.util;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
@@ -27,7 +29,27 @@ import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
  * passes basic semantic validation and we were able to extract dependency
  * information.
  */
+@Beta
 public final class ASTSchemaSource implements SchemaSourceRepresentation {
+    public static final Function<ASTSchemaSource, SourceIdentifier> GET_IDENTIFIER = new Function<ASTSchemaSource, SourceIdentifier>() {
+        @Override
+        public SourceIdentifier apply(final ASTSchemaSource input) {
+            return input.getIdentifier();
+        }
+    };
+    public static final Function<ASTSchemaSource, YangModelDependencyInfo> GET_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
+        @Override
+        public YangModelDependencyInfo apply(final ASTSchemaSource input) {
+            return input.getDependencyInformation();
+        }
+    };
+    public static final Function<ASTSchemaSource, ParserRuleContext> GET_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
+        @Override
+        public ParserRuleContext apply(final ASTSchemaSource input) {
+            return input.getAST();
+        }
+    };
+
     private final YangModelDependencyInfo depInfo;
     private final ParserRuleContext tree;
     private final SourceIdentifier id;
index ce9d6a8ce9331ea2bf1d0149257cb17e46fc630b..211e6a21743a666065e5e0ebc27aebb5cbb8de87 100644 (file)
@@ -6,26 +6,24 @@
  */
 package org.opendaylight.yangtools.yang.parser.util;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Charsets;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.io.CharStreams;
 import com.google.common.io.InputSupplier;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListeningExecutorService;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.concurrent.Callable;
 
 import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
-import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.SchemaSourceTransformer;
 import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.slf4j.Logger;
@@ -34,73 +32,43 @@ import org.slf4j.LoggerFactory;
 /**
  * A {@link SchemaSourceTransformer} which handles translation of models from
  * {@link YangTextSchemaSource} representation into {@link ASTSchemaSource}.
- *
- * While this class is currently used explicitly, its long-term purpose is to
- * be registered with a {@link org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry}
- * and be invoked on demand when the processing pipeline requests the
- * ASTSchemaSource representation.
  */
-public final class TextToASTTransformer implements SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
-    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
-    private static final Function<Exception, SchemaSourceTransformationException> MAPPER = new ExceptionMapper<SchemaSourceTransformationException>("Source transformation", SchemaSourceTransformationException.class) {
+@Beta
+public final class TextToASTTransformer extends SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
+    public static final class TextToASTTransformation implements Transformation<YangTextSchemaSource, ASTSchemaSource> {
         @Override
-        protected SchemaSourceTransformationException newWithCause(final String message, final Throwable cause) {
-            return new SchemaSourceTransformationException(message, cause);
-        }
-    };
-
-    private final ListeningExecutorService executor;
-
-    private TextToASTTransformer(final ListeningExecutorService executor) {
-        this.executor = Preconditions.checkNotNull(executor);
-    }
-
-    public static final TextToASTTransformer create(final ListeningExecutorService executor) {
-        return new TextToASTTransformer(executor);
-    }
+        public CheckedFuture<ASTSchemaSource, SchemaSourceException> apply(final YangTextSchemaSource input) throws IOException, YangSyntaxErrorException {
+            try (InputStream is = input.openStream()) {
+                final YangContext ctx = YangParserImpl.parseYangSource(is);
+                LOG.debug("Model {} parsed successfully", input);
 
-    @Override
-    public Class<YangTextSchemaSource> getInputRepresentation() {
-        return YangTextSchemaSource.class;
-    }
-
-    @Override
-    public Class<ASTSchemaSource> getOutputRepresentation() {
-        return ASTSchemaSource.class;
-    }
+                final ParseTreeWalker walker = new ParseTreeWalker();
+                final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
+                walker.walk(validator, ctx);
+                LOG.debug("Model {} validated successfully", input);
 
-    @Override
-    public CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> transformSchemaSource(final YangTextSchemaSource source) {
-        return Futures.makeChecked(executor.submit(new Callable<ASTSchemaSource>() {
-            @Override
-            public ASTSchemaSource call() throws IOException, YangSyntaxErrorException {
-                try (InputStream is = source.openStream()) {
-                    final YangContext ctx = YangParserImpl.parseYangSource(is);
-                    LOG.debug("Model {} parsed successfully", source);
+                // Backwards compatibility
+                final String text = CharStreams.toString(
+                        CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
+                            @Override
+                            public InputStream getInput() throws IOException {
+                                return input.openStream();
+                            }
+                        }, Charsets.UTF_8));
 
-                    final ParseTreeWalker walker = new ParseTreeWalker();
-                    final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
-                    walker.walk(validator, ctx);
-                    LOG.debug("Model {} validated successfully", source);
+                return Futures.immediateCheckedFuture(ASTSchemaSource.create(input.getIdentifier().getName(), ctx, text));
+            }
+        }
+    };
 
-                    // Backwards compatibility
-                    final String text = CharStreams.toString(
-                            CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
-                                @Override
-                                public InputStream getInput() throws IOException {
-                                    return source.openStream();
-                                }
-                            }, Charsets.UTF_8));
+    public static final TextToASTTransformation TRANSFORMATION = new TextToASTTransformation();
+    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
 
-                    return ASTSchemaSource.create(source.getIdentifier().getName(), ctx, text);
-                }
-            }
-        }), MAPPER);
+    private TextToASTTransformer(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        super(provider, YangTextSchemaSource.class, consumer, ASTSchemaSource.class, TRANSFORMATION);
     }
 
-    @Override
-    public int getCost() {
-        // We perform a direct translation, so the cost is 1.
-        return 1;
+    public static final TextToASTTransformer create(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        return new TextToASTTransformer(provider, consumer);
     }
 }