/* * Copyright 2017 The GDL Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.s1ck.gdl; import org.antlr.v4.runtime.ANTLRErrorStrategy; import org.antlr.v4.runtime.ANTLRFileStream; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.DefaultErrorStrategy; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.s1ck.gdl.model.Edge; import org.s1ck.gdl.model.Graph; import org.s1ck.gdl.model.Vertex; import org.s1ck.gdl.model.predicates.Predicate; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.Map; import java.util.Optional; /** * Helper class that wraps ANTLR initialization logic. */ public class GDLHandler { /** * GDL listener implementation. */ private GDLLoader loader; /** * Private constructor to avoid external initialization. * * @param loader GDL loader */ private GDLHandler(GDLLoader loader) { this.loader = loader; } /** * Append the given GDL string to the current database. * * @param asciiString GDL string (must not be {@code null}). */ public void append(String asciiString) { if (asciiString == null) { throw new IllegalArgumentException("AsciiString must not be null"); } ANTLRInputStream antlrInputStream = new ANTLRInputStream(asciiString); GDLLexer lexer = new GDLLexer(antlrInputStream); GDLParser parser = new GDLParser(new CommonTokenStream(lexer)); // update the loader state while walking the parse tree new ParseTreeWalker().walk(loader, parser.database()); } /** * Returns a collection of all graphs defined in the GDL script. * * @return graph collection */ public Collection<Graph> getGraphs() { return loader.getGraphs(); } /** * Returns a collection of all vertices defined in the GDL script. * * @return vertex collection */ public Collection<Vertex> getVertices() { return loader.getVertices(); } /** * Returns a collection of all edges defined in the GDL script. * * @return edge collection */ public Collection<Edge> getEdges() { return loader.getEdges(); } /** * Returns the predicates defined by the query in CNF. * * @return predicates */ public Optional<Predicate> getPredicates() { return loader.getPredicates(); } /** * Returns a cache that contains a mapping from user-defined variables to graph instances. * * @return immutable graph cache */ public Map<String, Graph> getGraphCache() { return loader.getGraphCache(); } /** * Returns a cache that contains a mapping from variables to graph instances. * * @param includeUserDefined true, iff user-defined variables shall be included in the cache * @param includeAutoGenerated true, iff auto-generated variables shall be included in the cache * * @return immutable graph cache */ public Map<String, Graph> getGraphCache(boolean includeUserDefined, boolean includeAutoGenerated) { return loader.getGraphCache(includeUserDefined, includeAutoGenerated); } /** * Returns a cache that contains a mapping from user-defined variables to vertex instances. * * @return immutable vertex cache */ public Map<String, Vertex> getVertexCache() { return loader.getVertexCache(); } /** * Returns a cache that contains a mapping from variables to vertex instances. * * @param includeUserDefined true, iff user-defined variables shall be included in the cache * @param includeAutoGenerated true, iff auto-generated variables shall be included in the cache * * @return immutable vertex cache */ public Map<String, Vertex> getVertexCache(boolean includeUserDefined, boolean includeAutoGenerated) { return loader.getVertexCache(includeUserDefined, includeAutoGenerated); } /** * Returns a cache that contains a mapping from user-defined variables to edge instances. * * @return immutable edge cache */ public Map<String, Edge> getEdgeCache() { return loader.getEdgeCache(); } /** * Returns a cache that contains a mapping from variables to edge instances. * * @param includeUserDefined true, iff user-defined variables shall be included in the cache * @param includeAutoGenerated true, iff auto-generated variables shall be included in the cache * * @return immutable edge cache */ public Map<String, Edge> getEdgeCache(boolean includeUserDefined, boolean includeAutoGenerated) { return loader.getEdgeCache(includeUserDefined, includeAutoGenerated); } /** * Builds a GDL Handler. */ public static class Builder { /** * Graph label. */ private String graphLabel = "__GRAPH"; /** * Vertex label. */ private String vertexLabel = "__VERTEX"; /** * Edge label. */ private String edgeLabel = "__EDGE"; /** * Flag to indicate if the default graph label shall be used if none is present. */ private boolean useDefaultGraphLabel = true; /** * Flag to indicate if the default vertex label shall be used if none is present. */ private boolean useDefaultVertexLabel = true; /** * Flag to indicate if the default edge label shall be used if none is present. */ private boolean useDefaultEdgeLabel = true; /** * Strategy for handling parser errors. */ private ANTLRErrorStrategy errorStrategy = new DefaultErrorStrategy(); /** * Default graph label is used if none is set in the GDL script. * * @param graphLabel graph label (must not be {@code null}). * @return builder */ public Builder setDefaultGraphLabel(String graphLabel) { this.graphLabel = graphLabel; return this; } /** * Default vertex label is used if none is set in the GDL script. * * @param vertexLabel vertex label (must not be {@code null}). * @return builder */ public Builder setDefaultVertexLabel(String vertexLabel) { this.vertexLabel = vertexLabel; return this; } /** * Default edge label is used if none is set in the GDL script. * * @param edgeLabel edge label (must not be {@code null}). * @return builder */ public Builder setDefaultEdgeLabel(String edgeLabel) { this.edgeLabel = edgeLabel; return this; } /** * Enable default graph label. * * @return builder */ public Builder enableDefaultGraphLabel() { this.useDefaultGraphLabel = true; return this; } /** * Disable default graph label. * * @return builder */ public Builder disableDefaultGraphLabel() { this.useDefaultGraphLabel = false; return this; } /** * Enable default vertex label. * * @return builder */ public Builder enableDefaultVertexLabel() { this.useDefaultVertexLabel = true; return this; } /** * Disable default vertex label. * * @return builder */ public Builder disableDefaultVertexLabel() { this.useDefaultVertexLabel = false; return this; } /** * Enable default edge label. * * @return builder */ public Builder enableDefaultEdgeLabel() { this.useDefaultEdgeLabel = true; return this; } /** * Disable default edge label. * * @return builder */ public Builder disableDefaultEdgeLabel() { this.useDefaultEdgeLabel = false; return this; } /** * Set the error handler strategy for ANTLR. If not set, {@link DefaultErrorStrategy} is used. * * @param errorStrategy ANTLR error strategy * @return builder */ public Builder setErrorStrategy(ANTLRErrorStrategy errorStrategy) { this.errorStrategy = errorStrategy; return this; } /** * Initialize GDL Handler from given ASCII String. * * @param asciiString GDL string (must not be {@code null}). * @return GDL handler */ public GDLHandler buildFromString(String asciiString) { ANTLRInputStream antlrInputStream = new ANTLRInputStream(asciiString); return build(antlrInputStream); } /** * Initializes GDL Handler from given input stream. * * @param stream InputStream (must not be {@code null}). * @return GDL handler * @throws IOException */ public GDLHandler buildFromStream(InputStream stream) throws IOException { ANTLRInputStream antlrInputStream = new ANTLRInputStream(stream); return build(antlrInputStream); } /** * Initializes GDL Handler from given file. * * @param fileName GDL file (must not be {@code null}). * @return GDL handler */ public GDLHandler buildFromFile(String fileName) throws IOException { ANTLRInputStream antlrInputStream = new ANTLRFileStream(fileName); return build(antlrInputStream); } /** * Checks valid input and creates GDL Handler. * * @param antlrInputStream ANTLR input stream * @return GDL handler */ private GDLHandler build(ANTLRInputStream antlrInputStream) { if (graphLabel == null) { throw new IllegalArgumentException("Graph label must not be null."); } if (vertexLabel == null) { throw new IllegalArgumentException("Vertex label must not be null."); } if (edgeLabel == null) { throw new IllegalArgumentException("Edge label must not be null."); } if (errorStrategy == null) { throw new IllegalArgumentException("Error handler must not be null."); } GDLLexer lexer = new GDLLexer(antlrInputStream); GDLParser parser = new GDLParser(new CommonTokenStream(lexer)); parser.setErrorHandler(errorStrategy); GDLLoader loader = new GDLLoader(graphLabel, vertexLabel, edgeLabel, useDefaultGraphLabel, useDefaultVertexLabel, useDefaultEdgeLabel); new ParseTreeWalker().walk(loader, parser.database()); return new GDLHandler(loader); } } }