/*
* Copyright 2008 Fedora Commons, Inc.
*
* 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.mulgara.protocol;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.jrdf.graph.BlankNode;
import org.jrdf.graph.Literal;
import org.jrdf.graph.URIReference;
import org.mulgara.query.Answer;
import org.mulgara.query.TuplesException;
import org.mulgara.query.Variable;
/**
* Represents an Answer as TQL XML.
*
* @created Jul 8, 2008
* @author Paula Gearon
* @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a>
*/
public class StreamedTqlXMLAnswer extends AbstractStreamedXMLAnswer {
/**
* Creates an XML Answer for XML results. Pretty printing is off by default.
* @param answer The Answer to wrap.
*/
public StreamedTqlXMLAnswer(Answer answer, OutputStream output) {
super(answer, output);
setPrettyPrint(false);
}
/**
* Creates an XML Answer for XML results. Pretty printing is off by default.
* @param answer The Answer to wrap.
*/
public StreamedTqlXMLAnswer(List<Answer> answers, OutputStream output) {
super(answers, output);
setPrettyPrint(false);
}
/** {@inheritDoc} */
public void addDocHeader() throws IOException {
s.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
s.append("<answer xmlns=\"http://mulgara.org/tql#\">");
}
/** {@inheritDoc} */
public void addDocFooter() throws IOException {
s.append(i(0)).append("</answer>");
}
/** {@inheritDoc} */
protected void addHeader(Answer answer) throws IOException {
s.append(i(1)).append("<query>");
addHeader(answer, 0);
}
void addHeader(Answer a, int indent) throws IOException {
s.append(i(indent + 2)).append("<variables>");
if (a != null && a.getVariables() != null) for (Variable v: a.getVariables()) addHeaderVariable(v, indent);
s.append(i(indent + 2)).append("</variables>");
}
protected void addFooter(Answer answer) throws IOException {
addFooter(answer, 0);
}
void addFooter(Answer answer, int indent) throws IOException {
s.append(i(indent + 1)).append("</query>");
}
/** {@inheritDoc} */
protected void addHeaderVariable(Variable var) throws IOException {
addHeaderVariable(var, 0);
}
/** {@inheritDoc} */
protected void addHeaderVariable(Variable var, int indent) throws IOException {
s.append(i(indent + 3)).append("<").append(var.getName()).append("/>");
}
/** {@inheritDoc} */
protected void addResults(Answer a) throws TuplesException, IOException {
a.beforeFirst();
while (a.next()) addResult(a);
}
/**
* Just like {@link #addResults(Answer)} only this is used for subanswers,
* so it may be indented, and does not have a trailing end of query section.
* @param a The current answer to get results from.
* @param indent The level of indentation to use.
*/
protected void addResults(Answer a, int indent) throws TuplesException, IOException {
a.beforeFirst();
while (a.next()) addResult(a, indent);
}
/** {@inheritDoc} */
protected void addResult(Answer a) throws TuplesException, IOException {
addResult(a, 0);
}
/**
* Prints a single row from an Answer, using the given indent.
* @param a The answer to get the row from.
* @param indent The indentation to use on the answer.
*/
protected void addResult(Answer a, int indent) throws TuplesException, IOException {
int width = (a != null) ? a.getNumberOfVariables() : 0;
Variable[] vars = (a != null) ? a.getVariables() : null;
s.append(i(indent + 2)).append("<solution>");
for (int c = 0; c < width; c++) addBinding(vars[c], a.getObject(c), indent);
s.append(i(indent + 2)).append("</solution>");
}
/**
* {@inheritDoc}
* No binding will be emitted if the value is null (unbound).
*/
protected void addBinding(Variable var, Object value) throws TuplesException, IOException {
addBinding(var, value, 0);
}
/**
* {@inheritDoc}
* No binding will be emitted if the value is null (unbound).
* @throws TuplesException Indicates an error accessing the Answer.
*/
protected void addBinding(Variable var, Object value, int indent) throws TuplesException, IOException {
if (value != null) {
s.append(i(indent + 3)).append("<").append(var.getName());
// no dynamic dispatch, so use if/then
if (value instanceof URIReference) {
addURI((URIReference)value);
} else if (value instanceof BlankNode) {
addBNode((BlankNode)value);
} else if (value instanceof Literal) {
addLiteral((Literal)value);
s.append("</").append(var.getName()).append(">");
} else if (value instanceof Answer) {
s.append(">");
addHeader((Answer)value, indent + 4);
addResults((Answer)value, indent + 4);
s.append("</").append(var.getName()).append(">");
((Answer)value).close();
} else throw new IllegalArgumentException("Unable to create a SPARQL response with an answer containing: " + value.getClass().getSimpleName());
}
}
/** {@inheritDoc} */
protected void addURI(URIReference uri) throws IOException {
s.append(" resource=\"").append(uri.getURI().toString()).append("\"/>");
}
/** {@inheritDoc} */
protected void addBNode(BlankNode bnode) throws IOException {
s.append(" blank-node=\"").append(bnode.toString()).append("\"/>");
}
/** {@inheritDoc} */
protected void addLiteral(Literal literal) throws IOException {
if (literal.getLanguage() != null) s.append(" language=\"").append(literal.getLanguage()).append("\"");
else if (literal.getDatatype() != null) s.append(" datatype=\"").append(literal.getDatatype().toString()).append("\"");
s.append(">").append(literal.getLexicalForm());
}
/**
* @see org.mulgara.protocol.StreamedAnswer#addAnswer(org.mulgara.query.Answer)
*/
public void addAnswer(Answer data) throws TuplesException, IOException {
addHeader(data);
addResults(data);
addFooter(data);
}
}