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

spark.protocol.parser.XMLSelectResults Maven / Gradle / Ivy

Go to download

Defines an implementation of the spark-api that uses the SPARQL protocol to query remote SPARQL endpoints via HTTP.

There is a newer version: 0.1.7
Show newest version
/*
 * Copyright 2010 Paul Gearon.
 *
 * 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 spark.protocol.parser;

import static javax.xml.stream.XMLStreamConstants.CHARACTERS;
import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
import static spark.protocol.parser.XMLResultsParser.Element.BINDING;
import static spark.protocol.parser.XMLResultsParser.Element.RESULT;
import static spark.protocol.parser.XMLResultsParser.Element.RESULTS;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import spark.api.Command;
import spark.api.Solutions;
import spark.api.exception.SparqlException;
import spark.api.rdf.RDFNode;
import spark.protocol.ProtocolCommand;
import spark.protocol.ProtocolResult;
import spark.protocol.parser.XMLResultsParser.Element;
import spark.spi.StreamingSolutions;
import spark.spi.rdf.BlankNodeImpl;
import spark.spi.rdf.NamedNodeImpl;
import spark.spi.rdf.PlainLiteralImpl;
import spark.spi.rdf.TypedLiteralImpl;

/**
 * Parses an XML stream for SPARQL results, returning them as solution mappings.
 * Based on StAX to allow for streaming large answers. The StAX cursor API is used
 * to make it easy to reuse the code from jSPARQLc.
 * 
 * Based on jSPARQLc, Copyright 2010 Paul Gearon.
 * http://code.google.com/p/jsparqlc/
 * jSPARQLc is licensed under the Apache License, Version 2.0.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * @author Paul Gearon
 */
public class XMLSelectResults extends StreamingSolutions implements Solutions, ProtocolResult {

  /** The list of metadata links for this result set. */
  private final List metadata;

  /** The XML reader and parser. */
  private final XMLStreamReader reader;

  private static final String VAR_NAME = "name";
  private static final String XML_NS = "http://www.w3.org/XML/1998/namespace";
  private static final String LANG = "lang";
  private static final String DATATYPE = "datatype";

  // Maintain a one-row lookahead to support isLast()
  private Map nextRow;
  
  /**
   * Creates a results object from an already opened reader that has had the header parsed to determine
   * that this contains a SPARQL XML result set.
   * @param stream The InputStream returned from a SPARQL endpoint.
   * @param query The query that led to the results.
   * @throws SparqlException If there was an error accessing the XML stream.
   */
  XMLSelectResults(Command cmd, XMLStreamReader reader, List columns, List metadata) throws SparqlException {
    super(cmd, columns);
    this.reader = reader;
    this.metadata = Collections.unmodifiableList(metadata);
    this.nextRow = readNext();
  }

  /** @return The metadata. */
  public List getMetadata() {
    return metadata;
  }

  @Override
  public void close() throws SparqlException, IOException {
    super.close();
    try {
      reader.close();
    } catch (XMLStreamException e) {
      throw new SparqlException("Error closing stream", e);
    } finally {
      Command c = getCommand();
      // Need this check because command can be null when testing the parser...
      if (c != null && c instanceof ProtocolCommand) {
        ((ProtocolCommand)c).release();
      }
    }
  }

  @Override
  public boolean isLast() {
    return currentRow != null && nextRow == null;
  }

  @Override
  protected Map fetchNext() throws SparqlException {
    Map row = nextRow;
    if (row != null) {
      nextRow = readNext();
    }
    return row;
  }

  /**
   * Parse the input stream to look for a result.
   * @return A new Row based on a single result from the results section.
   * @throws XMLStreamException There was an error reading the XML stream.
   * @throws SparqlException The XML was not valid SPARQL results.
   */
  protected Map readNext() throws SparqlException {
    try {
      // read  or 
      int eventType = reader.nextTag();
      // if a closing element, then it should be 
      if (eventType == END_ELEMENT) {
        // already read the final result, so clean up and return nothing
        if (nameIs(RESULTS)) {
          cleanup();
          return null;
        }
        else throw new SparqlException("Bad element closure with: " + reader.getLocalName());
      }
      // we only expect a  here 
      testOpen(eventType, RESULT, "Expected a new result. Got :" +
          ((eventType == END_ELEMENT) ? "/" : "") + reader.getLocalName());

      Map result = new HashMap();
      // read  list
      while ((eventType = reader.nextTag()) == START_ELEMENT && nameIs(BINDING)) {
        // get the name of the binding
        String name = reader.getAttributeValue(null, VAR_NAME);
        result.put(name, parseValue());
        testClose(reader.nextTag(), BINDING, "Single Binding not closed correctly");
      }

      // a non-  was read, so it should have been a 
      testClose(eventType, RESULT, "Single Result not closed correctly");
      return result;
    } catch (XMLStreamException e) {
      throw new SparqlException("Error reading from XML stream", e);
    }
  }

  /**
   * Parses the value for a variable binding. The data is either a URI, a BNode, or a Literal
   * as per {@link http://www.w3.org/TR/rdf-sparql-XMLres/#vb-results}
   * @return The parsed RDFNode.
   * @throws SparqlException If there was a consistency error in the parsed data.
   * @throws XMLStreamException If there was an error accessing the XML.
   */
  RDFNode parseValue() throws SparqlException, XMLStreamException {
    if (reader.nextTag() != START_ELEMENT) throw new SparqlException("No value in variable binding");
    Element elt = Element.valueOf(reader.getLocalName().toUpperCase());
    try {
      switch (elt) {
      case URI:
        if (reader.next() != CHARACTERS) throw new SparqlException("Unexpected data in URI binding");
        return new NamedNodeImpl(new URI(reader.getText()));
      case BNODE:
        if (reader.next() != CHARACTERS) throw new SparqlException("Unexpected data in BNode binding");
        return new BlankNodeImpl(reader.getText());
      case LITERAL:
        String dt = reader.getAttributeValue(null, DATATYPE);
        URI datatype = (dt == null) ? null : new URI(dt);
        String lang = reader.getAttributeValue(XML_NS, LANG);
        if (reader.next() != CHARACTERS) throw new SparqlException("Unexpected data in Literal binding");
        String lex = reader.getText();
        return (datatype != null) ? new TypedLiteralImpl(lex, datatype) : new PlainLiteralImpl(lex, lang);
      default:
        throw new SparqlException("Unexpected binding value: " + reader.getLocalName());
      }
    } catch (URISyntaxException e) {
      e.printStackTrace();
      throw new SparqlException("Bad URI in binding: " + e.getMessage());
    } catch (Throwable t) {
      t.printStackTrace();
      throw new SparqlException(t);
    } finally {
      testClose(reader.nextTag(), elt, "Bad close of value in binding");
    }
  }

  /**
   * Steps over the end of the XML. Invalid data will not cause an exception to be thrown, but the
   * error will be recorded.
   * @throws XMLStreamException If there was an error accessing the XML.
   */
  private void cleanup() throws XMLStreamException {
    XMLResultsParser.cleanup(reader);
  }

  /**
   * Convenience method to test if the read element is an opening tag with the correct name.
   * @param type The kind of element to test.
   * @param elt The element name.
   * @param message The message to use in case of error.
   * @throws SparqlException Thrown if the type is not a START_ELEMENT or the name is not the required name.
   */
  protected final void testOpen(int type, Element elt, String message) throws SparqlException {
    XMLResultsParser.testOpen(reader, type, elt, message);
  }

  /**
   * Convenience method to test if the read element is a closing tag with the correct name.
   * @param type The kind of element to test.
   * @param elt The element name.
   * @param message The message to use in case of error.
   * @throws SparqlException Thrown if the type is not an END_ELEMENT or the name is not the required name.
   */
  protected final void testClose(int type, Element elt, String message) throws SparqlException {
    XMLResultsParser.testClose(reader, type, elt, message);
  }

  /**
   * Convenience method to test if the current local name is the same as an expected element.
   * @param elt The element to test against.
   * @return true iff the current local name is the same as the element name.
   */
  protected final boolean nameIs(Element elt) {
    return XMLResultsParser.nameIs(reader, elt);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy