/*
* 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.helpers;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.openrdf.annotations.Bind;
import org.openrdf.annotations.Iri;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.repository.object.ObjectConnection;
import org.openrdf.repository.object.ObjectFactory;
import org.openrdf.repository.object.advisers.helpers.SparqlEvaluator.SparqlBuilder;
/**
* Stores the binding names, type and default values for a method with @Sparql
* annotation as read from the parameter annotations.
*/
public class SparqlParameters {
private final Type[] ptypes;
private final String[][] bindingNames;
private final String[] defaults;
public SparqlParameters(Method m) {
this.ptypes = m.getGenericParameterTypes();
this.bindingNames = getBindingNames(m.getParameterAnnotations());
this.defaults = getDefaultValues(m.getParameterAnnotations());
}
public Class<?> getComponentClass(Class<?> cls, Type type) {
return asClass(getComponentType(cls, type));
}
public SparqlBuilder populate(Object[] args, SparqlBuilder with, ObjectConnection con) {
for (int i = 0; i < args.length && i < bindingNames.length; i++) {
Object value = args[i];
Type vtype = ptypes[i];
Class<?> cvtype = asClass(vtype);
String defaultValue = defaults[i];
if (value == null && defaultValue != null && con != null) {
value = getDefaultValue(defaultValue, vtype, con);
}
if (value == null)
continue;
if (Set.class.equals(cvtype)) {
for (String name : bindingNames[i]) {
with = with.with(name, (Set<?>) value);
}
} else {
for (String name : bindingNames[i]) {
with = with.with(name, value);
}
}
}
return with;
}
private Object getDefaultValue(String value, Type type, ObjectConnection con) {
Class<?> ctype = asClass(type);
if (Set.class.equals(ctype)) {
Object v = getDefaultValue(value, getComponentType(ctype, type), con);
if (v == null)
return null;
return Collections.singleton(v);
}
ValueFactory vf = con.getValueFactory();
ObjectFactory of = con.getObjectFactory();
if (of.isDatatype(ctype)) {
URIImpl datatype = new URIImpl("java:" + ctype.getName());
return of.createValue(of.createObject(new LiteralImpl(value, datatype)));
}
return vf.createURI(value);
}
private Type getComponentType(Class<?> cls, Type type) {
if (cls.isArray())
return cls.getComponentType();
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
Type[] args = ptype.getActualTypeArguments();
return args[args.length - 1];
}
if (Set.class.equals(cls) || Map.class.equals(cls))
return Object.class;
return null;
}
private Class<?> asClass(Type type) {
if (type == null)
return null;
if (type instanceof Class<?>)
return (Class<?>) type;
if (type instanceof GenericArrayType) {
GenericArrayType atype = (GenericArrayType) type;
Class<?> componentType = asClass(atype.getGenericComponentType());
return Array.newInstance(asClass(componentType), 0).getClass();
}
if (type instanceof ParameterizedType) {
return asClass(((ParameterizedType) type).getRawType());
}
return Object.class; // wildcard
}
private String[][] getBindingNames(Annotation[][] anns) {
String[][] bindingNames = new String[anns.length][];
loop: for (int i=0; i<anns.length; i++) {
bindingNames[i] = new String[0];
for (Annotation ann : anns[i]) {
if (Bind.class.equals(ann.annotationType())) {
bindingNames[i] = ((Bind) ann).value();
continue loop;
} else if (Iri.class.equals(ann.annotationType())) {
bindingNames[i] = new String[] { local(((Iri) ann).value()) };
}
}
}
return bindingNames;
}
private String local(String iri) {
String string = iri;
if (string.lastIndexOf('#') >= 0) {
string = string.substring(string.lastIndexOf('#') + 1);
}
if (string.lastIndexOf('?') >= 0) {
string = string.substring(string.lastIndexOf('?') + 1);
}
if (string.lastIndexOf('/') >= 0) {
string = string.substring(string.lastIndexOf('/') + 1);
}
if (string.lastIndexOf(':') >= 0) {
string = string.substring(string.lastIndexOf(':') + 1);
}
return string;
}
private String[] getDefaultValues(Annotation[][] anns) {
String[] defaults = new String[anns.length];
for (int i=0; i<anns.length; i++) {
Object value = getDefaultValue(anns[i]);
if (value != null) {
defaults[i] = value.toString();
}
}
return defaults;
}
private Object getDefaultValue(Annotation[] anns) {
for (Annotation ann : anns) {
for (Method m : ann.annotationType().getDeclaredMethods()) {
Iri iri = m.getAnnotation(Iri.class);
if (iri != null && OWL.HASVALUE.equals(iri.value()) && m.getParameterTypes().length == 0) {
return invoke(m, ann);
}
}
}
return null;
}
private Object invoke(Method m, Object obj) {
try {
return m.invoke(obj);
} catch (IllegalArgumentException e) {
throw new AssertionError(e);
} catch (IllegalAccessException e) {
IllegalAccessError error = new IllegalAccessError(e.getMessage());
error.initCause(e);
throw error;
} catch (InvocationTargetException e) {
try {
throw e.getCause();
} catch (RuntimeException cause) {
throw cause;
} catch (Error cause) {
throw cause;
} catch (Throwable cause) {
throw new UndeclaredThrowableException(cause);
}
}
}
}