/*
 * 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.InputStream;

import org.apache.jena.atlas.io.IO;
import org.apache.jena.irix.IRIs;
import org.apache.jena.irix.IRIx;
import org.apache.jena.riot.system.stream.StreamManager;
import org.apache.jena.shared.NotFoundException;
import org.apache.jena.sparql.lang.ParserARQ ;
import org.apache.jena.sparql.lang.SPARQLParser ;
import org.apache.jena.sparql.lang.SPARQLParserRegistry ;
import org.apache.jena.sparql.syntax.Element ;
import org.apache.jena.sparql.syntax.Template ;

public class QueryFactory
{
    /** Create a SPARQL query from the given string.
     *
     * @param queryString      The query string
     * @throws QueryException  Thrown when a parse error occurs
     */

    static public Query create(String queryString)
    {
        return create(queryString, Syntax.defaultQuerySyntax) ;
    }

    /** Create a query from the given string with the
     *
     * @param queryString      The query string
     * @param syntax           {@link Syntax}
     * @throws QueryException  Thrown when a parse error occurs
     */

    static public Query create(String queryString, Syntax syntax)
    {
        return create(queryString, null, syntax) ;
    }

    /** Create a query from the given string by calling the parser.
     *
     * @param queryString      The query string
     * @param baseURI          Base URI
     * @throws QueryException  Thrown when a parse error occurs
     */

    static public Query create(String queryString, String baseURI)
    {
        Query query = new Query() ;
        return parse(query, queryString, baseURI, Syntax.defaultQuerySyntax) ;
    }

    /** Create a query from the given string by calling the parser.
    *
    * @param queryString      The query string
    * @param baseURI          Base URI
    * @param syntax           {@link Syntax}
    * @throws QueryException  Thrown when a parse error occurs
    */

   static public Query create(String queryString, String baseURI, Syntax syntax)
   {
       Query query = new Query() ;
       return parse(query, queryString, baseURI, syntax) ;
   }

   /**
    * Make a query - no parsing done
    */
   static public Query create() { return new Query() ; }


   /**
     * Make a query - no parsing done - old name: {@link #create()} preferred.
     */
    static public Query make() { return create() ; }

    /**
     * Make a query from another one by deep copy (a clone).
     * The returned query will be .equals to the original.
     * The returned query can be mutated without changing the
     * original (at which point it will stop being .equals)
     *
     * @param originalQuery  The query to clone.
     *
     */

    static public Query create(Query originalQuery)
    {
        return originalQuery.cloneQuery() ;
    }

    /** Parse a query from the given string by calling the parser.
     *
     * @param query            Existing, uninitialized query
     * @param queryString      The query string
     * @param baseURI          URI for relative URI expansion
     * @param syntaxURI        URI for the syntax
     * @throws QueryException  Thrown when a parse error occurs
     */

    static public Query parse(Query query, String queryString, String baseURI, Syntax syntaxURI)
    {
        if ( syntaxURI == null )
            syntaxURI = query.getSyntax() ;
        else
            query.setSyntax(syntaxURI) ;

        SPARQLParser parser = SPARQLParser.createParser(syntaxURI) ;

        if ( parser == null )
            throw new UnsupportedOperationException("Unrecognized syntax for parsing: "+syntaxURI) ;

        if ( query.getBase() == null )
        {
            IRIx queryBase = null;
            try {
                queryBase = ( baseURI != null ) ? IRIs.resolveIRI(baseURI) : IRIs.getSystemBase();
            } catch (Exception ex) {}
            if ( queryBase == null )
                queryBase = IRIx.create("http://localhost/query/defaultBase#");
            query.setBase(queryBase);

        }
        return parser.parse(query, queryString) ;
    }

    static boolean knownParserSyntax(Syntax syntaxURI)
    {
        return SPARQLParserRegistry.get().containsFactory(syntaxURI) ;
    }


    /**
     * Read a SPARQL query from a file.
     *
     * @param url
     *            URL (file: or http: or anything a FileManager can handle)
     * @return A new query object
     */
    static public Query read(String url)
    {
        return read(url, (StreamManager)null, null, null) ;
    }

    /** Read a SPARQL query from a file.
     *
     * @param url            URL (file: or http: or anything a FileManager can handle)
     * @param baseURI        BaseURI for the query
     * @return               A new query object
     */
    static public Query read(String url, String baseURI)
    {
        return read(url, (StreamManager)null, baseURI, null) ;
    }

    /** Read a query from a file.
     *
     * @param url            URL (file: or http: or anything a FileManager can handle)
     * @param langURI        Query syntax
     * @return               A new query object
     */
    static public Query read(String url, Syntax langURI)
    {
        return read(url, (StreamManager)null, null, langURI) ;
    }

    /** Read a query from a file.
     *
     * @param url            URL (file: or http: or anything a FileManager can handle)
     * @param baseURI        BaseURI for the query
     * @param langURI        Query syntax
     * @return               A new query object
     */
    static public Query read(String url, String baseURI, Syntax langURI)
    {
        return read(url, (StreamManager)null, baseURI, langURI) ;
    }

    /** Read a query from a file.
     *
     * @param url              URL (file: or http: or anything a FileManager can handle)
     * @param streamManager    Optional StreamManager
     * @param baseURI          BaseURI for the query
     * @param langURI          Query syntax
     * @return                 A new query object
     */
    static public Query read(String url, StreamManager streamManager, String baseURI, Syntax langURI)
    {
        if ( streamManager == null )
            streamManager = StreamManager.get() ;

        InputStream in = streamManager.open(url);
        if ( in == null )
            throw new NotFoundException("Not found: "+url);
        String qStr = IO.readWholeFileAsUTF8(streamManager.open(url));
        if ( baseURI == null )
            baseURI = url ;
        if ( langURI == null )
            langURI = Syntax.guessFileSyntax(url) ;

        return create(qStr, baseURI, langURI) ;
    }


    static public Element createElement(String elementString)
    {
        return ParserARQ.parseElement(elementString) ;
    }

    static public Template createTemplate(String templateString)
    {
        return ParserARQ.parseTemplate(templateString) ;
    }
}
