/* * Copyright (c) 2012 3 Round Stones Inc., Some rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the openrdf.org nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package org.openrdf.repository.object.advisers; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.nio.channels.ReadableByteChannel; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamException; import javax.xml.transform.TransformerException; import org.openrdf.OpenRDFException; import org.openrdf.model.BNode; import org.openrdf.model.Literal; import org.openrdf.model.Model; import org.openrdf.model.Resource; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.query.BindingSet; import org.openrdf.query.GraphQueryResult; import org.openrdf.query.TupleQueryResult; import org.openrdf.repository.object.ObjectConnection; import org.openrdf.repository.object.RDFObject; import org.openrdf.repository.object.advice.Advice; import org.openrdf.repository.object.advisers.helpers.SparqlEvaluator; import org.openrdf.repository.object.advisers.helpers.SparqlParameters; import org.openrdf.repository.object.advisers.helpers.SparqlEvaluator.SparqlBuilder; import org.openrdf.repository.object.traits.ObjectMessage; import org.openrdf.repository.object.traits.Refreshable; import org.openrdf.result.Result; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; /** * Executes a SPARQL query/update operation when a method with @Sparql is called. */ public class SparqlAdvice implements Advice { private final SparqlEvaluator evaluator; private final Class<?> returnClass; private final Class<?> componentClass; private final Map<Method, SparqlParameters> map = new HashMap<Method, SparqlParameters>(); public SparqlAdvice(SparqlEvaluator evaluator, Method m) { this.evaluator = evaluator; this.returnClass = m.getReturnType(); Type rtype = m.getGenericReturnType(); SparqlParameters parameters = new SparqlParameters(m); this.componentClass = parameters.getComponentClass(returnClass, rtype); map.put(m, parameters); } @Override public String toString() { return evaluator.toString(); } public Object intercept(ObjectMessage message) throws Exception { Object target = message.getTarget(); ObjectConnection con = ((RDFObject) target).getObjectConnection(); Resource self = ((RDFObject) target).getResource(); SparqlBuilder with = evaluator.prepare(con).with("this", self); Object[] args = message.getParameters(); getParameters(message.getMethod()).populate(args, with, con); if (isUpdate()) { with.asUpdate(); if (target instanceof Refreshable) { ((Refreshable) target).refresh(); } return message.proceed(); } else { Object result = cast(with, returnClass, componentClass); if (result == null) return message.proceed(); if (returnClass.isPrimitive() && result.equals(nil(returnClass))) return message.proceed(); return result; } } private synchronized SparqlParameters getParameters(Method m) { if (map.containsKey(m)) return map.get(m); SparqlParameters parameters = new SparqlParameters(m); map.put(m, parameters); return parameters; } private boolean isUpdate() { return Void.class.equals(returnClass) || Void.TYPE.equals(returnClass); } private Object cast(SparqlBuilder result, Class<?> rclass, Class<?> componentClass) throws OpenRDFException, TransformerException, IOException, ParserConfigurationException, SAXException, XMLStreamException { if (TupleQueryResult.class.equals(rclass)) { return result.asTupleQueryResult(); } else if (GraphQueryResult.class.equals(rclass)) { return result.asGraphQueryResult(); } else if (Result.class.equals(rclass)) { return result.asResult(componentClass); } else if (Set.class.equals(rclass)) { return result.asSet(componentClass); } else if (List.class.equals(rclass)) { return result.asList(componentClass); } else if (byte[].class.equals(rclass)) { return result.asByteArray(); } else if (CharSequence.class.equals(rclass)) { return result.asCharSequence(); } else if (Readable.class.equals(rclass)) { return result.asReadable(); } else if (String.class.equals(rclass)) { return result.asString(); } else if (Void.class.equals(rclass) || Void.TYPE.equals(rclass)) { result.asUpdate(); return null; } else if (Boolean.class.equals(rclass) || Boolean.TYPE.equals(rclass)) { return result.asBoolean(); } else if (Byte.class.equals(rclass) || Byte.TYPE.equals(rclass)) { return result.asByte(); } else if (Character.class.equals(rclass) || Character.TYPE.equals(rclass)) { return result.asChar(); } else if (Double.class.equals(rclass) || Double.TYPE.equals(rclass)) { return result.asDouble(); } else if (Float.class.equals(rclass) || Float.TYPE.equals(rclass)) { return result.asFloat(); } else if (Integer.class.equals(rclass) || Integer.TYPE.equals(rclass)) { return result.asInt(); } else if (Long.class.equals(rclass) || Long.TYPE.equals(rclass)) { return result.asLong(); } else if (Short.class.equals(rclass) || Short.TYPE.equals(rclass)) { return result.asShort(); } else if (Model.class.equals(rclass)) { return result.asModel(); } else if (Statement.class.equals(rclass)) { return result.asStatement(); } else if (BindingSet.class.equals(rclass)) { return result.asBindingSet(); } else if (URI.class.equals(rclass)) { return result.asURI(); } else if (BNode.class.equals(rclass)) { return result.asBNode(); } else if (Literal.class.equals(rclass)) { return result.asLiteral(); } else if (Resource.class.equals(rclass)) { return result.asResource(); } else if (Value.class.equals(rclass)) { return result.asValue(); } else if (Document.class.equals(rclass)) { return result.asDocument(); } else if (DocumentFragment.class.equals(rclass)) { return result.asDocumentFragment(); } else if (Element.class.equals(rclass)) { return result.asElement(); } else if (Node.class.equals(rclass)) { return result.asNode(); } else if (Reader.class.equals(rclass)) { return result.asReader(); } else if (CharArrayWriter.class.equals(rclass)) { return result.asCharArrayWriter(); } else if (ByteArrayOutputStream.class.equals(rclass)) { return result.asByteArrayOutputStream(); } else if (ReadableByteChannel.class.equals(rclass)) { return result.asReadableByteChannel(); } else if (InputStream.class.equals(rclass)) { return result.asInputStream(); } else if (XMLEventReader.class.equals(rclass)) { return result.asXMLEventReader(); } else { return result.as(rclass); } } private Object nil(Class<?> type) { if (Set.class.equals(type)) return Collections.emptySet(); if (!type.isPrimitive()) return null; if (Void.TYPE.equals(type)) return null; if (Boolean.TYPE.equals(type)) return Boolean.FALSE; if (Character.TYPE.equals(type)) return Character.valueOf((char) 0); if (Byte.TYPE.equals(type)) return Byte.valueOf((byte) 0); if (Short.TYPE.equals(type)) return Short.valueOf((short) 0); if (Integer.TYPE.equals(type)) return Integer.valueOf((int) 0); if (Long.TYPE.equals(type)) return Long.valueOf((long) 0); if (Float.TYPE.equals(type)) return Float.valueOf((float) 0); if (Double.TYPE.equals(type)) return Double.valueOf((double) 0); throw new AssertionError(); } }