BUG-997: Introduce ASTSchemaSource 69/9369/8
authorRobert Varga <rovarga@cisco.com>
Fri, 25 Jul 2014 02:45:53 +0000 (04:45 +0200)
committerRobert Varga <rovarga@cisco.com>
Thu, 31 Jul 2014 10:46:58 +0000 (12:46 +0200)
ASTSchemaSource represents an intermediate processing step, where we
have parsed the YANG text and extracted dependency information.

Change-Id: Ice0b841404a28912787b83f88f9b2d28c68497d5
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java [new file with mode: 0644]

index 0002c11187b5a723c482e85ca5efdc194c8430ef..bf163b9ec7a56569bdf0e1e0c68ae5971b7e45a4 100644 (file)
@@ -583,31 +583,29 @@ public final class YangParserImpl implements YangContextParser {
         }
     }
 
-    private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
+    private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException, YangSyntaxErrorException {
         final Map<ByteSource, ParseTree> trees = new HashMap<>();
         for (ByteSource source : sources) {
-            trees.put(source, parseYangSource(source));
+            try (InputStream stream = source.openStream()) {
+                trees.put(source, parseYangSource(stream));
+            }
         }
         return trees;
     }
 
-    private YangContext parseYangSource(final ByteSource source) throws IOException, YangSyntaxErrorException {
-        try (InputStream stream = source.openStream()) {
-            final ANTLRInputStream input = new ANTLRInputStream(stream);
-            final YangLexer lexer = new YangLexer(input);
-            final CommonTokenStream tokens = new CommonTokenStream(lexer);
-            final YangParser parser = new YangParser(tokens);
-            parser.removeErrorListeners();
+    public static YangContext parseYangSource(final InputStream stream) throws IOException, YangSyntaxErrorException {
+        final YangLexer lexer = new YangLexer(new ANTLRInputStream(stream));
+        final CommonTokenStream tokens = new CommonTokenStream(lexer);
+        final YangParser parser = new YangParser(tokens);
+        parser.removeErrorListeners();
 
-            final YangErrorListener errorListener = new YangErrorListener();
-            parser.addErrorListener(errorListener);
+        final YangErrorListener errorListener = new YangErrorListener();
+        parser.addErrorListener(errorListener);
 
-            final YangContext result = parser.yang();
-            errorListener.validate();
+        final YangContext result = parser.yang();
+        errorListener.validate();
 
-            return result;
-        }
+        return result;
     }
 
     /**
index fcf0b8924c15fc3ab81a6e908b4d77173683f412..97590779b2a75934a46c8f6d19ad1f015c04ef17 100644 (file)
@@ -11,9 +11,12 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.ge
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
+
 import java.io.InputStream;
 import java.util.Date;
 import java.util.List;
+
+import org.antlr.v4.runtime.ParserRuleContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
@@ -25,6 +28,7 @@ import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtCont
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 /**
@@ -53,7 +57,7 @@ public abstract class YangModelDependencyInfo {
     private final ImmutableSet<ModuleImport> dependencies;
 
     YangModelDependencyInfo(final String name, final String formattedRevision,
-                            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
         this.name = name;
         this.formattedRevision = formattedRevision;
         this.revision = QName.parseRevision(formattedRevision);
@@ -142,6 +146,29 @@ public abstract class YangModelDependencyInfo {
         return true;
     }
 
+    /**
+     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
+     * of a YANG model.
+     *
+     * @param tree Abstract syntax tree
+     * @return {@link YangModelDependencyInfo}
+     * @throws YangSyntaxErrorException
+     *             If the AST is not a valid YANG module/submodule
+     */
+    public static YangModelDependencyInfo fromAST(final String name, final ParserRuleContext tree) throws YangSyntaxErrorException {
+        final Optional<Module_stmtContext> moduleCtx = getFirstContext(tree, Module_stmtContext.class);
+        if (moduleCtx.isPresent()) {
+            return parseModuleContext(moduleCtx.get());
+        }
+
+        final Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
+        if (submoduleCtx.isPresent()) {
+            return parseSubmoduleContext(submoduleCtx.get());
+        }
+
+        throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
+    }
+
     /**
      * Extracts {@link YangModelDependencyInfo} from input stream
      * containing YANG model.
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java
new file mode 100644 (file)
index 0000000..fb5aa9b
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.util;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+import javax.annotation.Nonnull;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+
+/**
+ * Abstract Syntax Tree representation of a schema source. This representation
+ * is internal to the YANG parser implementation, as it relies on ANTLR types.
+ *
+ * Instances of this representation are used for caching purposes, as they
+ * are a natural intermediate step in YANG text processing pipeline: the text
+ * has been successfully parsed, so we know it is syntactically correct. It also
+ * passes basic semantic validation and we were able to extract dependency
+ * information.
+ */
+public final class ASTSchemaSource implements SchemaSourceRepresentation {
+    private final YangModelDependencyInfo depInfo;
+    private final ParserRuleContext tree;
+    private final SourceIdentifier id;
+
+    private ASTSchemaSource(final @Nonnull SourceIdentifier id, @Nonnull final ParserRuleContext tree, final @Nonnull YangModelDependencyInfo depInfo) {
+        this.depInfo = Preconditions.checkNotNull(depInfo);
+        this.tree = Preconditions.checkNotNull(tree);
+        this.id = Preconditions.checkNotNull(id);
+    }
+
+    /**
+     * Create a new instance of AST representation for a abstract syntax tree,
+     * performing minimal semantic analysis to acquire dependency information.
+     *
+     * @param name YANG source name. Used only for error reporting.
+     * @param tree ANTLR abstract syntax tree
+     * @return A new representation instance.
+     * @throws YangSyntaxErrorException if we fail to extract dependency information.
+     */
+    public static final ASTSchemaSource create(final @Nonnull String name, final @Nonnull ParserRuleContext tree) throws YangSyntaxErrorException {
+        final YangModelDependencyInfo depInfo = YangModelDependencyInfo.fromAST(name, tree);
+        final SourceIdentifier id = new SourceIdentifier(depInfo.getName(), Optional.of(depInfo.getFormattedRevision()));
+        return new ASTSchemaSource(id, tree, depInfo);
+    }
+
+    @Override
+    public SourceIdentifier getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public Class<? extends SchemaSourceRepresentation> getType() {
+        return ASTSchemaSource.class;
+    }
+
+    /**
+     * Return the underlying abstract syntax tree.
+     *
+     * @return Underlying AST.
+     */
+    public @Nonnull ParserRuleContext getAST() {
+        return tree;
+    }
+
+    /**
+     * Return the dependency information as extracted from the AST.
+     *
+     * FIXME: this method should be extracted into a public interface in the
+     *        model.api.repo class, relying solely on model.api types.
+     *
+     * @return Dependency information.
+     */
+    public @Nonnull YangModelDependencyInfo getDependencyInformation() {
+        return depInfo;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java
new file mode 100644 (file)
index 0000000..38c0dc3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.util;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+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.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.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+/**
+ * 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 Function<Exception, SchemaSourceTransformationException> MAPPER = new ExceptionMapper<SchemaSourceTransformationException>("Source transformation", SchemaSourceTransformationException.class) {
+        @Override
+        protected SchemaSourceTransformationException newWithCause(final String message, final Throwable cause) {
+            return new SchemaSourceTransformationException(message, cause);
+        }
+    };
+    private final ListeningExecutorService executor;
+
+    TextToASTTransformer(final ListeningExecutorService executor) {
+        this.executor = Preconditions.checkNotNull(executor);
+    }
+
+    @Override
+    public Class<YangTextSchemaSource> getInputRepresentation() {
+        return YangTextSchemaSource.class;
+    }
+
+    @Override
+    public Class<ASTSchemaSource> getOutputRepresentation() {
+        return ASTSchemaSource.class;
+    }
+
+    @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);
+                    return ASTSchemaSource.create(source.getIdentifier().getName(), ctx);
+                }
+            }
+        }), MAPPER);
+    }
+
+    @Override
+    public int getCost() {
+        // We perform a direct translation, so the cost is 1.
+        return 1;
+    }
+}