All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jena.query.Query Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.jena.query;

import java.io.OutputStream ;
import java.util.* ;

import org.apache.jena.atlas.io.IndentedLineBuffer ;
import org.apache.jena.atlas.io.IndentedWriter ;
import org.apache.jena.atlas.io.Printable ;
import org.apache.jena.atlas.logging.Log ;
import org.apache.jena.graph.Node ;
import org.apache.jena.sparql.ARQConstants ;
import org.apache.jena.sparql.algebra.table.TableData ;
import org.apache.jena.sparql.core.* ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.expr.Expr ;
import org.apache.jena.sparql.expr.ExprAggregator ;
import org.apache.jena.sparql.expr.ExprVar ;
import org.apache.jena.sparql.expr.aggregate.Aggregator ;
import org.apache.jena.sparql.serializer.QuerySerializerFactory ;
import org.apache.jena.sparql.serializer.SerializerRegistry ;
import org.apache.jena.sparql.syntax.Element ;
import org.apache.jena.sparql.syntax.PatternVars ;
import org.apache.jena.sparql.syntax.Template ;
import org.apache.jena.sparql.util.FmtUtils ;
import org.apache.jena.sys.JenaSystem ;

/** The data structure for a query as presented externally.
 *  There are two ways of creating a query - use the parser to turn
 *  a string description of the query into the executable form, and
 *  the programmatic way (the parser is calling the programmatic
 *  operations driven by the query string).  The declarative approach
 *  of passing in a string is preferred.
 *
 * Once a query is built, it can be passed to the QueryFactory to produce a query execution engine.
 * @see QueryExecutionFactory
 * @see ResultSet */

public class Query extends Prologue implements Cloneable, Printable
{
    static { JenaSystem.init() ; /* Ensure everything has started properly */ }
    
    public static final int QueryTypeUnknown    = -123 ;
    public static final int QueryTypeSelect     = 111 ;
    public static final int QueryTypeConstruct  = 222 ;
    public static final int QueryTypeDescribe   = 333 ;
    public static final int QueryTypeAsk        = 444 ;
    public static final int QueryTypeJson       = 555 ;
    int queryType = QueryTypeUnknown ; 
    
    // If no model is provided explicitly, the query engine will load
    // a model from the URL.  Never a list of zero items.
    
    private List graphURIs = new ArrayList<>() ;
    private List namedGraphURIs = new ArrayList<>() ;
    
    // The WHERE clause
    private Element queryPattern = null ;
    
    // Query syntax
    private Syntax syntax = Syntax.syntaxSPARQL ; // Default
    
    // LIMIT/OFFSET
    public static final long  NOLIMIT = Long.MIN_VALUE ;
    private long resultLimit   = NOLIMIT ;
    private long resultOffset  = NOLIMIT ;
    
    // ORDER BY
    private List orderBy       = null ;
    public static final int ORDER_ASCENDING           = 1 ; 
    public static final int ORDER_DESCENDING          = -1 ;
    public static final int ORDER_DEFAULT             = -2 ;    // Not explicitly given. 
    public static final int ORDER_UNKNOW              = -3 ; 

    // VALUES trailing clause
    protected TableData valuesDataBlock = null ;
    
    protected boolean strictQuery = true ;
    
    // SELECT * seen
    protected boolean queryResultStar        = false ;
    
    protected boolean distinct               = false ;
    protected boolean reduced                = false ;
    
    // CONSTRUCT
    protected Template constructTemplate  = null ;
    
    // DESCRIBE
    // Any URIs/QNames in the DESCRIBE clause
    // Also uses resultVars
    protected List resultNodes               = new ArrayList<>() ;     // Type in list: Node
    
    /**
     * Creates a new empty query
     */
    public Query()
    {
        syntax = Syntax.syntaxSPARQL ;
    }
    
    /**
     * Creates a new empty query with the given prologue
     */
    public Query(Prologue prologue)
    {
        this() ;
        usePrologueFrom(prologue) ;
    }
    
    // Allocate variables that are unique to this query.
    private VarAlloc varAlloc = new VarAlloc(ARQConstants.allocVarMarker) ;
    private Var allocInternVar() { return varAlloc.allocVar() ; }
    
    //private VarAlloc varAnonAlloc = new VarAlloc(ARQConstants.allocVarAnonMarker) ;
    //public Var allocVarAnon() { return varAnonAlloc.allocVar() ; }
    
    public void setQuerySelectType()            { queryType = QueryTypeSelect ; }
    public void setQueryConstructType()         { queryType = QueryTypeConstruct ; queryResultStar = true ; }
    public void setQueryDescribeType()          { queryType = QueryTypeDescribe ; }
    public void setQueryAskType()               { queryType = QueryTypeAsk ; }
    public void setQueryJsonType()              { queryType = QueryTypeJson ; }
    
    public int getQueryType()                   { return queryType ; }
    
    public boolean isSelectType()               { return queryType == QueryTypeSelect ; }

    public boolean isConstructType()            { return queryType == QueryTypeConstruct ; }

    public boolean isDescribeType()             { return queryType == QueryTypeDescribe ; }

    public boolean isAskType()                  { return queryType == QueryTypeAsk ; }

    public boolean isJsonType()                 { return queryType == QueryTypeJson ; }

    public boolean isUnknownType()              { return queryType == QueryTypeUnknown ; }
    
    public boolean isConstructQuad()            { return isConstructType() && constructTemplate.containsRealQuad() ; }
    // It was a mistake to extend Prologue ... but what is done is done.
    public Prologue getPrologue()               { return this ; }
    
    public void setStrict(boolean isStrict)
    { 
        strictQuery = isStrict ;
        
        if ( strictQuery )
            initStrict() ;
        else
            initLax() ;
    }
    
    public boolean isStrict()                { return strictQuery ; }
    
    private void initStrict()
    {
//        if ( prefixMap.getGlobalPrefixMapping() == globalPrefixMap )
//            prefixMap.setGlobalPrefixMapping(null) ;
    }

    private void initLax()
    {
//        if ( prefixMap.getGlobalPrefixMapping() == null )
//            prefixMap.setGlobalPrefixMapping(globalPrefixMap) ;
    }
    
    public void setDistinct(boolean b) { distinct = b ; }
    public boolean isDistinct()        { return distinct ; }
    
    public void setReduced(boolean b) { reduced = b ; }
    public boolean isReduced()        { return reduced ; }
    
    /** @return Returns the syntax. */
    public Syntax getSyntax()         { return syntax ; }

    /** @param syntax The syntax to set. */
    public void setSyntax(Syntax syntax)
    { 
        this.syntax = syntax ;
        if ( syntax != Syntax.syntaxSPARQL )
            strictQuery = false ;
    }

    // ---- Limit/offset
    
    public long getLimit()             { return resultLimit ; } 
    public void setLimit(long limit)   { resultLimit = limit ; }
    public boolean hasLimit()          { return resultLimit != NOLIMIT ; }
    
    public long getOffset()            { return resultOffset ; } 
    public void setOffset(long offset) { resultOffset = offset ; }
    public boolean hasOffset()         { return resultOffset != NOLIMIT ; }
    
    // ---- Order By
    
    public boolean hasOrderBy()        { return orderBy != null && orderBy.size() > 0 ; }
    
    public boolean isOrdered()         { return hasOrderBy() ; }

    public void addOrderBy(SortCondition condition)
    {
        if ( orderBy == null )
            orderBy = new ArrayList<>() ;

        orderBy.add(condition) ;
    }
    public void addOrderBy(Expr expr, int direction)
    {
        SortCondition sc = new SortCondition(expr, direction) ;
        addOrderBy(sc) ;
    }
    
    public void addOrderBy(Node var, int direction)
    { 
        if ( ! var.isVariable() )
            throw new QueryException("Not a variable: "+var) ;
        SortCondition sc = new SortCondition(var, direction) ;
        addOrderBy(sc) ;
    }
    
    public void addOrderBy(String varName, int direction)
    { 
        varName = Var.canonical(varName) ;
        SortCondition sc = new SortCondition(new ExprVar(varName), direction) ;
        addOrderBy(sc) ;
    }

    public List getOrderBy()           { return orderBy ; }
    
    // ---- 
    
    /** Answer whether the query had SELECT/DESCRIBE/CONSTRUCT *
     * @return boolean as to whether a * result form was seen
     */ 
    public boolean isQueryResultStar() { return queryResultStar ; }

    /** Set whether the query had SELECT/DESCRIBE *
     * 
     * @param isQueryStar 
     */
    public void setQueryResultStar(boolean isQueryStar)
    {
        queryResultStar = isQueryStar ;
        if ( isQueryStar ) 
            resultVarsSet = false ;
    }
    
    public void setQueryPattern(Element elt)
    {
        queryPattern = elt ;
    }
    
    public Element getQueryPattern() { return queryPattern ; }
    
     /** Location of the source for the data.  If the model is not set,
     *  then the QueryEngine will attempt to load the data from these URIs
     *  into the default (unamed) graph.
     */
    public void addGraphURI(String s)
    {
        if ( graphURIs == null )
            graphURIs = new ArrayList<>() ;
        graphURIs.add(s) ;
    }

    /** Location of the source for the data.  If the model is not set,
     *  then the QueryEngine will attempt to load the data from these URIs
     *  as named graphs in the dataset.
     */
    public void addNamedGraphURI(String uri)
    {
        if ( namedGraphURIs == null )
            namedGraphURIs = new ArrayList<>() ;
        if ( namedGraphURIs.contains(uri) )
            throw new QueryException("URI already in named graph set: "+uri) ;
        else
            namedGraphURIs.add(uri) ;
    }
    
    /** Return the list of URIs (strings) for the unnamed graph
     * 
     * @return List of strings
     */
    
    public List getGraphURIs() { return graphURIs ; }

    /** Test whether the query mentions a URI in forming the default graph (FROM clause) 
     * 
     * @param uri
     * @return boolean  True if the URI used in a FROM clause 
     */
    public boolean usesGraphURI(String uri) { return graphURIs.contains(uri) ; }
    
    /** Return the list of URIs (strings) for the named graphs (FROM NAMED clause)
     * 
     * @return List of strings
     */
    
    public List getNamedGraphURIs() { return namedGraphURIs ; }

    /** Test whether the query mentions a URI for a named graph. 
     * 
     * @param uri
     * @return True if the URI used in a FROM NAMED clause 
     */
    public boolean usesNamedGraphURI(String uri) { return namedGraphURIs.contains(uri) ; }
    
    /** Return true if the query has either some graph
     * URIs or some named graph URIs in its description.
     * This does not mean these URIs will be used - just that
     * they are noted as part of the query. 
     */ 
    
    public boolean hasDatasetDescription()
    {
        if ( getGraphURIs() != null && getGraphURIs().size() > 0 )
            return true ;
        if ( getNamedGraphURIs() != null && getNamedGraphURIs().size() > 0 )
            return true ;
        return false ;
    }
    
    /** Return a dataset description (FROM/FROM NAMED clauses) for the query. */  
    public DatasetDescription getDatasetDescription()
    {
        if ( ! hasDatasetDescription() )
            return null;
        
        DatasetDescription description = new DatasetDescription() ;
        
        description.addAllDefaultGraphURIs(getGraphURIs()) ;
        description.addAllNamedGraphURIs(getNamedGraphURIs()) ;
        return description ;
    }
    
    // ---- SELECT

    protected VarExprList projectVars = new VarExprList() ;

    /** Return a list of the variables requested (SELECT) */
    public List getResultVars()
    { 
        // Ensure "SELECT *" processed
        setResultVars() ;
        return Var.varNames(projectVars.getVars()) ;
    }
    
    /** Return a list of the variables requested (SELECT) */
    public List getProjectVars()
    { 
        // Ensure "SELECT *" processed
        setResultVars() ;
        return projectVars.getVars() ;
    }
    
    public VarExprList getProject()
    {
        return projectVars ;
    }
    
    /** Add a collection of projection variables to a SELECT query */
    public void addProjectVars(Collection vars)
    {
        for ( Object obj : vars )
        {
            if ( obj instanceof String )
            {
                this.addResultVar( (String) obj );
                continue;
            }
            if ( obj instanceof Var )
            {
                this.addResultVar( (Var) obj );
                continue;
            }
            throw new QueryException( "Not a variable or variable name: " + obj );
        }
        resultVarsSet = true ;
    }

    
    /** Add a projection variable to a SELECT query */
    public void addResultVar(String varName)
    {
        varName = Var.canonical(varName) ;
        _addResultVar(varName) ;
    }

    public void addResultVar(Node v)
    {
        if ( !v.isVariable() )
            throw new QueryException("Not a variable: "+v) ;
        _addResultVar(v.getName()) ;
    }
    
    public void addResultVar(Node v, Expr expr)
    {
        Var var = null ; 
        if ( v == null )
            var = allocInternVar() ;
        else
        {
            if ( !v.isVariable() )
                throw new QueryException("Not a variable: "+v) ;
            var = Var.alloc(v) ;
        }
        _addVarExpr(projectVars, var, expr) ;
    }
    
    /** Add an to a SELECT query (a name will be created for it) */
    public void addResultVar(Expr expr)
    {
        _addVarExpr(projectVars, allocInternVar(), expr) ;
    }

    /** Add a named expression to a SELECT query */
    public void addResultVar(String varName, Expr expr)
    {
        Var var = null ; 
        if ( varName == null )
            var = allocInternVar() ;
        else
        {
            varName = Var.canonical(varName) ;
            var = Var.alloc(varName) ;
        }
        _addVarExpr(projectVars, var, expr) ;
    }

    // Add raw name.
    private void _addResultVar(String varName)
    {
        Var v = Var.alloc(varName) ;
        _addVar(projectVars, v) ;
        resultVarsSet = true ;
    }

    private static void _addVar(VarExprList varExprList, Var v)
    {
        if ( varExprList.contains(v) )
        {
            Expr expr = varExprList.getExpr(v) ;
            if ( expr != null )
                
                // SELECT (?a+?b AS ?x) ?x
                throw new QueryBuildException("Duplicate variable (had an expression) in result projection '"+v+"'") ;
            // SELECT ?x ?x
            if ( ! ARQ.allowDuplicateSelectColumns )
                return ;
            // else drop thorugh and have two variables of the same name.
        }
        varExprList.add(v) ;
    }

    private static void _addVarExpr(VarExprList varExprList, Var v, Expr expr)
    {
        if ( varExprList.contains(v) )
            // SELECT ?x (?a+?b AS ?x)
            // SELECT (2*?a AS ?x) (?a+?b AS ?x)
            throw new QueryBuildException("Duplicate variable in result projection '"+v+"'") ;  
        varExprList.add(v, expr) ;
    }

    protected VarExprList groupVars = new VarExprList() ;
    protected List havingExprs = new ArrayList<>() ;  // Expressions : Make an ExprList?
    
    public boolean hasGroupBy()     { return ! groupVars.isEmpty() || getAggregators().size() > 0 ; }
    public boolean hasHaving()      { return havingExprs != null && havingExprs.size() > 0 ; }
    
    public VarExprList getGroupBy()      { return groupVars ; }
    
    public List getHavingExprs()    { return havingExprs ; }
    
    public void addGroupBy(String varName)
    {
        varName = Var.canonical(varName) ;
        addGroupBy(Var.alloc(varName)) ;
    }

    public void addGroupBy(Node v)
    {
        _addVar(groupVars, Var.alloc(v)) ;
    }

    public void addGroupBy(Expr expr) { addGroupBy(null, expr) ; }
    
    public void addGroupBy(Var v, Expr expr)
    {
        if ( v == null )
            v = allocInternVar() ;
        
        if ( expr.isVariable() && v.isAllocVar() )
        {
            // It was (?x) with no AS - keep the name by adding by variable.
            addGroupBy(expr.asVar()) ;
            return ;
        }
        
        groupVars.add(v, expr) ;
    }

    public void addHavingCondition(Expr expr)
    {
        havingExprs.add(expr) ;
    }

    // SELECT JSON

    private Map jsonMapping = new LinkedHashMap<>();

    public void addJsonMapping(String key, Node value) {
        jsonMapping.put(key, value);
    }

    public Map getJsonMapping() {
        return Collections.unmodifiableMap(jsonMapping);
    }

    // ---- Aggregates

    // Record allocated aggregations.
    // Later: The same aggregation expression used in a query 
    // will always lead to the same aggregator.
    // For now, allocate a fresh one each time (cause the calculation
    // to be done multiple times but (1) it's unusual to have repeated 
    // aggregators normally and (2) the actual calculation is cheap. 
        
    // Unlike SELECT expressions, here the expression itself (E_Aggregator) knows its variable
    // Commonality?
    
    private List aggregators = new ArrayList<>() ;
    private Map aggregatorsMap = new HashMap<>() ;
    
    // Note any E_Aggregator created for reuse.
    private Map aggregatorsAllocated = new HashMap<>() ;
    
    public boolean hasAggregators() { return aggregators.size() != 0  ; }
    public List getAggregators() { return aggregators ; }
    
    public Expr allocAggregate(Aggregator agg)
    {
        // We need to track the aggregators in case one aggregator is used twice, e.g. in HAVING and in SELECT expression
        // (is that much harm to do twice?  Yes, if distinct.)
        String key = agg.key() ;
        
        Var v = aggregatorsAllocated.get(key); 
        if ( v != null )
        {
            ExprAggregator eAgg = aggregatorsMap.get(v) ; 
            if ( ! agg.equals(eAgg.getAggregator()) )
                Log.warn(Query.class, "Internal inconsistency: Aggregator: "+agg) ;
            return eAgg ;
        }
        // Allocate.
        v = allocInternVar() ;
        ExprAggregator aggExpr = new ExprAggregator(v, agg) ;
        aggregatorsAllocated.put(key, v) ;
        aggregatorsMap.put(v, aggExpr) ;
        aggregators.add(aggExpr) ;
        return aggExpr ;
    }
    
    // ---- VALUES
    
    /** Does the query have a VALUES trailing block? */
    public boolean hasValues()                { return valuesDataBlock != null ; }
    
    /** Variables from a VALUES trailing block */
    public List getValuesVariables()     { return valuesDataBlock==null ? null : valuesDataBlock.getVars() ; }
    
    /** Data from a VALUES trailing block. null for a Node means undef */ 
    public List getValuesData()      { return valuesDataBlock==null ? null : valuesDataBlock.getRows() ; }

    public void setValuesDataBlock(List variables, List values)
    {
        checkDataBlock(variables, values) ;
        valuesDataBlock = new TableData(variables, values) ;
    }
    
    private static void checkDataBlock(List variables, List values)
    {
        // Check.
        int N = variables.size() ;
        for ( Binding valueRow : values )
        {
            Iterator iter= valueRow.vars() ;
            for ( ; iter.hasNext() ; )
            { 
                Var v = iter.next() ;
                if ( ! variables.contains(v) )
                    throw new QueryBuildException("Variable "+v+" not found in "+variables) ;
            }
        }
    }
    
    // ---- CONSTRUCT 
    
    /** Get the template pattern for a construct query */ 
    public Template getConstructTemplate() 
    { 
        return constructTemplate ;
    }
    
    /** Set triple patterns for a construct query */ 
    public void setConstructTemplate(Template templ)  { constructTemplate = templ ; }

    // ---- DESCRIBE
    
    public void addDescribeNode(Node node)
    {
        if ( node.isVariable() ) { addResultVar(node) ; return ; }
        if ( node.isURI() || node.isBlank() )
        {
            if ( !resultNodes.contains(node) )
                resultNodes.add(node);
            return ;
        }
        if ( node.isLiteral() )
            throw new QueryException("Result node is a literal: "+FmtUtils.stringForNode(node)) ;
        throw new QueryException("Result node not recognized: "+node) ;
    }

    
    /** Get the result list (things wanted - not the results themselves)
     *  of a DESCRIBE query. */ 
    public List getResultURIs() { return resultNodes ; }
    
    private boolean resultVarsSet = false ; 
    /** Fix up when the query has "*" (when SELECT * or DESCRIBE *)
     *  and for a construct query.  This operation is idempotent.
     */
    public void setResultVars()
    {
        if ( resultVarsSet )
            return ;
        resultVarsSet = true ;
        
        if ( getQueryPattern() == null )
        {
            if ( ! this.isDescribeType() )
                Log.warn(this, "setResultVars(): no query pattern") ;
            return ;
        }
        
        if ( isSelectType() )
        {
            if ( isQueryResultStar() )
                findAndAddNamedVars() ;
            return ;
        }
        
        if ( isConstructType() )
        {
            // All named variables are in-scope
            findAndAddNamedVars() ;
            return ;
        }
        
        if ( isDescribeType() )
        {
            if ( isQueryResultStar() )
                findAndAddNamedVars() ;
            return ;
        }

//        if ( isAskType() )
//        {}
    }
    
    private void findAndAddNamedVars()
    {
        Iterator varIter = null ;
        if ( hasGroupBy() )
            varIter = groupVars.getVars().iterator() ;
        else
        {
            // Binding variables -- in patterns, not in filters and not in EXISTS
            LinkedHashSet queryVars = new LinkedHashSet<>() ;
            PatternVars.vars(queryVars, this.getQueryPattern()) ;
            if ( this.hasValues() )
                queryVars.addAll(getValuesVariables()) ;
//            if ( this.hasValues() )
//                queryVars.addAll(getValuesVariables()) ;
            varIter = queryVars.iterator() ;
        }
        
        // All query variables, including ones from bNodes in the query.
        
        for ( ; varIter.hasNext() ; )
        {
            Object obj = varIter.next() ;
            //Var var = (Var)iter.next() ;
            Var var = (Var)obj ;
            if ( var.isNamedVar() )
                addResultVar(var) ;
        }
    }

    public void visit(QueryVisitor visitor)
    {
        visitor.startVisit(this) ;
        visitor.visitResultForm(this) ;
        visitor.visitPrologue(this) ;
        if ( this.isSelectType() )
            visitor.visitSelectResultForm(this) ;
        if ( this.isConstructType() )
            visitor.visitConstructResultForm(this) ;
        if ( this.isDescribeType() )
            visitor.visitDescribeResultForm(this) ;
        if ( this.isAskType() )
            visitor.visitAskResultForm(this) ;
        if ( this.isJsonType() )
            visitor.visitJsonResultForm(this) ;
        visitor.visitDatasetDecl(this) ;
        visitor.visitQueryPattern(this) ;
        visitor.visitGroupBy(this) ;
        visitor.visitHaving(this) ;
        visitor.visitOrderBy(this) ;
        visitor.visitOffset(this) ;
        visitor.visitLimit(this) ;
        visitor.visitValues(this) ;
        visitor.finishVisit(this) ;
    }

    @Override
    public Object clone() { return cloneQuery() ; }
    
    /**
     * Makes a copy of this query.  Copies by parsing a query from the serialized form of this query
     * @return Copy of this query
     */
    public Query cloneQuery() {
        // A little crude.
        // Must use toString() rather than serialize() because we may not know how to serialize extended syntaxes
        String qs = this.toString();
        return QueryFactory.create(qs, getSyntax()) ;
    }
    
    // ---- Query canonical syntax
    
    // Reverse of parsing : should produce a string that parses to an equivalent query
    // "Equivalent" => gives the same results on any model  
    @Override
    public String toString()
    { return serialize() ; }
    
    public String toString(Syntax syntax)
    { return serialize(syntax) ; }

    
    /** Must align with .equals */
    private int hashcode = -1 ;
    
    @Override
    public int hashCode()
    { 
        if ( hashcode == -1 )
        {
            hashcode = QueryHashCode.calc(this) ;
            if ( hashcode == -1 )
                hashcode = Integer.MIN_VALUE/2 ;
        }
        return hashcode ;
    }
    
    /** Are two queries equals - tests shape and details.
     * Equality means that the queries do the same thing, including
     * same variables, in the same places.  Being unequals does
     * not mean the queries do different things.  
     * 
     * For example, reordering a group or union
     * means that a query is different.
     *  
     * Two instances of a query parsed from the same string are equal. 
     */
    
    @Override
    public boolean equals(Object other)
    { 
        if ( ! ( other instanceof Query ) )
            return false ;
        if ( this == other ) return true ;
        return QueryCompare.equals(this, (Query)other) ;
    }
    
//    public static boolean sameAs(Query query1, Query query2)
//    { return query1.sameAs(query2) ; }  

    @Override
    public void output(IndentedWriter out)
    {
        serialize(out) ;
    }

    /** Convert the query to a string */
    
    public String serialize()
    {
        IndentedLineBuffer buff = new IndentedLineBuffer() ;
        serialize(buff) ;
        return buff.toString();
    }
    
    /** Convert the query to a string in the given syntax
     * @param syntax
     */
    
    public String serialize(Syntax syntax)
    {
        IndentedLineBuffer buff = new IndentedLineBuffer() ;
        serialize(buff, syntax) ;
        return buff.toString();
    }

    /** Output the query
     * @param out  OutputStream
     */
    public void serialize(OutputStream out) { serialize(out, syntax); }
    
    /** Output the query
     * 
     * @param out     OutputStream
     * @param syntax  Syntax URI
     */
    
    public void serialize(OutputStream out, Syntax syntax) { 
        IndentedWriter writer = new IndentedWriter(out) ;
        serialize(writer, syntax) ;
        writer.flush() ;
        try { out.flush() ; } catch (Exception ex) { } 
    }

    /** Format the query into the buffer
     * 
     * @param buff    IndentedLineBuffer
     */
    
    public void serialize(IndentedLineBuffer buff) { 
        serialize(buff, syntax); 
    }
    
    /** Format the query
     * 
     * @param buff       IndentedLineBuffer in which to place the unparsed query
     * @param outSyntax  Syntax URI
     */
    
    public void serialize(IndentedLineBuffer buff, Syntax outSyntax) { 
        serialize((IndentedWriter)buff, outSyntax);
    }

    /** Format the query
     * 
     * @param writer  IndentedWriter
     */
    
    public void serialize(IndentedWriter writer) { 
        serialize(writer, syntax); 
    }

    /** Format the query
     * 
     * @param writer     IndentedWriter
     * @param outSyntax  Syntax URI
     */
    
    public void serialize(IndentedWriter writer, Syntax outSyntax)
    {
        // Try to use a serializer factory if available
        QuerySerializerFactory factory = SerializerRegistry.get().getQuerySerializerFactory(outSyntax);
        QueryVisitor serializer = factory.create(outSyntax, this, writer);
        this.visit(serializer);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy