# SPARQLResource.java

    /*
     * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
     *
     * 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.qi4j.library.rest.admin;

    import info.aduna.xml.XMLWriter;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Enumeration;
    import org.openrdf.http.protocol.Protocol;
    import org.openrdf.model.URI;
    import org.openrdf.model.Value;
    import org.openrdf.query.*;
    import org.openrdf.query.impl.DatasetImpl;
    import org.openrdf.query.resultio.TupleQueryResultWriter;
    import org.openrdf.query.resultio.sparqljson.SPARQLResultsJSONWriterFactory;
    import org.openrdf.query.resultio.sparqlxml.SPARQLResultsXMLWriter;
    import org.openrdf.repository.Repository;
    import org.openrdf.repository.RepositoryConnection;
    import org.openrdf.repository.RepositoryException;
    import org.qi4j.api.injection.scope.Service;
    import org.restlet.data.Form;
    import org.restlet.data.MediaType;
    import org.restlet.data.Method;
    import org.restlet.data.Status;
    import org.restlet.representation.InputRepresentation;
    import org.restlet.representation.OutputRepresentation;
    import org.restlet.representation.Representation;
    import org.restlet.representation.Variant;
    import org.restlet.resource.ResourceException;
    import org.restlet.resource.ServerResource;

    import static org.openrdf.http.protocol.Protocol.*;

    /**
     * JAVADOC
     */
    public class SPARQLResource
        extends ServerResource
    {
        @Service
        Repository repository;

        public SPARQLResource()
        {
            getVariants().addAll( Arrays.asList(
                new Variant( MediaType.TEXT_HTML ),
                new Variant( MediaType.APPLICATION_RDF_XML ),
                new Variant( RestApplication.APPLICATION_SPARQL_JSON ) ) );
            setNegotiated( true );
        }

        @Override
        protected void doInit()
            throws ResourceException
        {
            super.doInit();
        }

        @Override
        public Representation get( final Variant variant )
            throws ResourceException
        {
            try
            {
                // TODO There's probably a helper somewhere that can do this more nicely
                if( getRequest().getOriginalRef().getLastSegment().equals( "sparqlhtml.xsl" ) )
                {
                    InputStream resourceAsStream = getClass().getResourceAsStream( "sparqlhtml.xsl" );
                    return new InputRepresentation( resourceAsStream, MediaType.TEXT_XML );
                }

                Form form;
                if( getRequest().getMethod().equals( Method.POST ) )
                {
                    form = new Form(getRequest().getEntity());
                }
                else
                {
                    form = getRequest().getResourceRef().getQueryAsForm();
                }

                final RepositoryConnection conn = repository.getConnection();

                String queryStr = form.getFirstValue( "query" );

                if( queryStr == null )
                {
                    InputStream resourceAsStream = getClass().getResourceAsStream( "sparqlform.html" );
                    return new InputRepresentation( resourceAsStream, MediaType.TEXT_HTML );
                }

                Query query = getQuery( repository, conn, queryStr );

                if( query instanceof TupleQuery )
                {
                    TupleQuery tQuery = (TupleQuery) query;

                    final TupleQueryResult queryResult = tQuery.evaluate();

                    if( variant.getMediaType().equals( MediaType.TEXT_HTML ) )
                    {
                        return new OutputRepresentation( MediaType.TEXT_XML )
                        {
                            @Override
                            public void write( OutputStream outputStream )
                                throws IOException
                            {
                                try
                                {
                                    PrintWriter out = new PrintWriter( outputStream );
                                    out.println( "<?xml version='1.0' encoding='UTF-8'?>" );
                                    out.println( "<?xml-stylesheet type=\"text/xsl\" href=\"query/sparqlhtml.xsl\"?>" );
                                    out.flush();
                                    TupleQueryResultWriter qrWriter = new SPARQLResultsXMLWriter( new XMLWriter( outputStream )
                                    {
                                        @Override
                                        public void startDocument()
                                            throws IOException
                                        {
                                            // Ignore
                                        }
                                    } );
                                    QueryResultUtil.report( queryResult, qrWriter );
                                }
                                catch( Exception e )
                                {
                                    throw new IOException( e );
                                }
                                finally
                                {
                                    try
                                    {
                                        conn.close();
                                    }
                                    catch( RepositoryException e )
                                    {
                                        // Ignore
                                    }
                                }
                            }
                        };
                    }
                    else if( variant.getMediaType().equals( MediaType.APPLICATION_RDF_XML ) )
                    {
                        return new OutputRepresentation( MediaType.APPLICATION_XML )
                        {
                            @Override
                            public void write( OutputStream outputStream )
                                throws IOException
                            {
                                try
                                {
                                    TupleQueryResultWriter qrWriter = new SPARQLResultsXMLWriter( new XMLWriter( outputStream ) );
                                    QueryResultUtil.report( queryResult, qrWriter );
                                }
                                catch( Exception e )
                                {
                                    throw new IOException( e );
                                }
                                finally
                                {
                                    try
                                    {
                                        conn.close();
                                    }
                                    catch( RepositoryException e )
                                    {
                                        // Ignore
                                    }
                                }
                            }
                        };
                    }
                    else if( variant.getMediaType().equals( RestApplication.APPLICATION_SPARQL_JSON ) )
                    {
                        return new OutputRepresentation( RestApplication.APPLICATION_SPARQL_JSON )
                        {
                            @Override
                            public void write( OutputStream outputStream )
                                throws IOException
                            {
                                try
                                {
                                    TupleQueryResultWriter qrWriter = new SPARQLResultsJSONWriterFactory().getWriter( outputStream );
                                    QueryResultUtil.report( queryResult, qrWriter );
                                }
                                catch( Exception e )
                                {
                                    throw new IOException( e );
                                }
                                finally
                                {
                                    try
                                    {
                                        conn.close();
                                    }
                                    catch( RepositoryException e )
                                    {
                                        // Ignore
                                    }
                                }
                            }
                        };
                    }
                }
                else if( query instanceof GraphQuery )
                {
                    GraphQuery gQuery = (GraphQuery) query;

                    /*
                                    queryResult = gQuery.evaluate();
                                    registry = RDFWriterRegistry.getInstance();
                                    view = GraphQueryResultView.getInstance();
                    */
                    conn.close();
                }
                else if( query instanceof BooleanQuery )
                {
                    BooleanQuery bQuery = (BooleanQuery) query;

                    /*
                                    queryResult = bQuery.evaluate();
                                    registry = BooleanQueryResultWriterRegistry.getInstance();
                                    view = BooleanQueryResultView.getInstance();
                    */
                    conn.close();
                }
                else
                {
                    conn.close();
                    throw new ResourceException( Status.CLIENT_ERROR_BAD_REQUEST, "Unsupported query type: "
                                                                                  + query.getClass().getName() );
                }
            }
            catch( RepositoryException e )
            {
                throw new ResourceException( Status.SERVER_ERROR_INTERNAL, e );
            }
            catch( QueryEvaluationException e )
            {
                throw new ResourceException( Status.SERVER_ERROR_INTERNAL, e );
            }

            return null;
        }

        private Query getQuery( Repository repository, RepositoryConnection repositoryCon, String queryStr
        )
            throws ResourceException
        {
            Form form = getRequest().getResourceRef().getQueryAsForm();

            Query result;

    // default query language is SPARQL
            QueryLanguage queryLn = QueryLanguage.SPARQL;

    // determine if inferred triples should be included in query evaluation
            boolean includeInferred = true;

    // build a dataset, if specified
            String[] defaultGraphURIs = form.getValuesArray( DEFAULT_GRAPH_PARAM_NAME );
            String[] namedGraphURIs = form.getValuesArray( NAMED_GRAPH_PARAM_NAME );

            DatasetImpl dataset = null;
            if( defaultGraphURIs.length > 0 || namedGraphURIs.length > 0 )
            {
                dataset = new DatasetImpl();

                if( defaultGraphURIs.length > 0 )
                {
                    for( String defaultGraphURI : defaultGraphURIs )
                    {
                        try
                        {
                            URI uri = repository.getValueFactory().createURI( defaultGraphURI );
                            dataset.addDefaultGraph( uri );
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new ResourceException( Status.CLIENT_ERROR_BAD_REQUEST, "Illegal URI for default graph: "
                                                                                          + defaultGraphURI );
                        }
                    }
                }

                if( namedGraphURIs.length > 0 )
                {
                    for( String namedGraphURI : namedGraphURIs )
                    {
                        try
                        {
                            URI uri = repository.getValueFactory().createURI( namedGraphURI );
                            dataset.addNamedGraph( uri );
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new ResourceException( Status.CLIENT_ERROR_BAD_REQUEST, "Illegal URI for named graph: "
                                                                                          + namedGraphURI );
                        }
                    }
                }
            }

            try
            {
                result = repositoryCon.prepareQuery( queryLn, queryStr );
                result.setIncludeInferred( includeInferred );

                if( dataset != null )
                {
                    result.setDataset( dataset );
                }

    // determine if any variable bindings have been set on this query.
                @SuppressWarnings( "unchecked" )
                Enumeration<String> parameterNames = Collections.enumeration( form.getValuesMap().keySet() );

                while( parameterNames.hasMoreElements() )
                {
                    String parameterName = parameterNames.nextElement();

                    if( parameterName.startsWith( BINDING_PREFIX ) && parameterName.length() > BINDING_PREFIX.length() )
                    {
                        String bindingName = parameterName.substring( BINDING_PREFIX.length() );
                        Value bindingValue = parseValueParam( repository, form, parameterName );
                        result.setBinding( bindingName, bindingValue );
                    }
                }
            }
            catch( UnsupportedQueryLanguageException e )
            {
                throw new ResourceException( Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage() );
            }
            catch( MalformedQueryException e )
            {
                throw new ResourceException( Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage() );
            }
            catch( RepositoryException e )
            {
                throw new ResourceException( Status.SERVER_ERROR_INTERNAL, e.getMessage() );
            }

            return result;
        }

        private Value parseValueParam( Repository repository, Form form, String parameterName )
            throws ResourceException
        {
            String paramValue = form.getFirstValue( parameterName );
            try
            {
                return Protocol.decodeValue( paramValue, repository.getValueFactory() );
            }
            catch( IllegalArgumentException e )
            {
                throw new ResourceException( Status.CLIENT_ERROR_BAD_REQUEST, "Invalid value for parameter '" + parameterName + "': "
                                                                              + paramValue );
            }
        }
    }