52n-wps-algorithm-gcube/src/main/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospec...

281 lines
12 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* 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.n52.wps.algorithm.annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.n52.wps.algorithm.annotation.AnnotationBinding.ExecuteMethodBinding;
import org.n52.wps.algorithm.annotation.AnnotationBinding.InputBinding;
import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputBinding;
import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataInputFieldAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataInputMethodAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataOutputFieldAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataOutputMethodAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.ExecuteAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.InputAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataInputFieldAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataInputMethodAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataOutputFieldAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataOutputMethodAnnotationParser;
import org.n52.wps.algorithm.annotation.AnnotationParser.OutputAnnotationParser;
import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author tkunicki
*/
public class AnnotatedAlgorithmIntrospector {
private final static Logger LOGGER = LoggerFactory.getLogger(AnnotatedAlgorithmIntrospector.class);
private final static List<InputAnnotationParser<?,Field,?>> INPUT_FIELD_PARSERS;
private final static List<InputAnnotationParser<?,Method,?>> INPUT_METHOD_PARSERS;
private final static List<OutputAnnotationParser<?,Field,?>> OUTPUT_FIELD_PARSERS;
private final static List<OutputAnnotationParser<?,Method,?>> OUTPUT_METHOD_PARSERS;
private final static ExecuteAnnotationParser PROCESS_PARSER;
static {
List<InputAnnotationParser<?,Field,?>> inputFieldParsers =
new ArrayList<InputAnnotationParser<?,Field,?>>();
inputFieldParsers.add(new LiteralDataInputFieldAnnotationParser());
inputFieldParsers.add(new ComplexDataInputFieldAnnotationParser());
INPUT_FIELD_PARSERS = Collections.unmodifiableList(inputFieldParsers);
List<InputAnnotationParser<?,Method,?>> inputMethodParsers =
new ArrayList<InputAnnotationParser<?,Method,?>>();
inputMethodParsers.add(new LiteralDataInputMethodAnnotationParser());
inputMethodParsers.add(new ComplexDataInputMethodAnnotationParser());
INPUT_METHOD_PARSERS = Collections.unmodifiableList(inputMethodParsers);
List<OutputAnnotationParser<?,Field,?>> outputFieldParsers =
new ArrayList<OutputAnnotationParser<?,Field,?>>();
outputFieldParsers.add(new LiteralDataOutputFieldAnnotationParser());
outputFieldParsers.add(new ComplexDataOutputFieldAnnotationParser());
OUTPUT_FIELD_PARSERS = Collections.unmodifiableList(outputFieldParsers);
List<OutputAnnotationParser<?,Method,?>> outputMethodParsers =
new ArrayList<OutputAnnotationParser<?,Method,?>>();
outputMethodParsers.add(new LiteralDataOutputMethodAnnotationParser());
outputMethodParsers.add(new ComplexDataOutputMethodAnnotationParser());
OUTPUT_METHOD_PARSERS = Collections.unmodifiableList(outputMethodParsers);
PROCESS_PARSER = new ExecuteAnnotationParser();
}
private final static Map<Class<?>, AnnotatedAlgorithmIntrospector> INTROSPECTOR_MAP =
new HashMap<Class<?>, AnnotatedAlgorithmIntrospector>();
public static synchronized AnnotatedAlgorithmIntrospector getInstrospector(Class<?> algorithmClass) {
AnnotatedAlgorithmIntrospector introspector = INTROSPECTOR_MAP.get(algorithmClass);
if (introspector == null) {
introspector = new AnnotatedAlgorithmIntrospector(algorithmClass);
INTROSPECTOR_MAP.put(algorithmClass, introspector);
}
return introspector;
}
private Class<?> algorithmClass;
private AlgorithmDescriptor algorithmDescriptor;
private ExecuteMethodBinding executeMethodBinding;
private Map<String, AnnotationBinding.InputBinding<?, ?>> inputBindingMap;
private Map<String, AnnotationBinding.OutputBinding<?, ?>> outputBindingMap;
public AnnotatedAlgorithmIntrospector(Class<?> algorithmClass) {
this.algorithmClass = algorithmClass;
inputBindingMap = new LinkedHashMap<String, InputBinding<?, ?>>();
outputBindingMap = new LinkedHashMap<String, OutputBinding<?, ?>>();
parseClass();
inputBindingMap = Collections.unmodifiableMap(inputBindingMap);
outputBindingMap = Collections.unmodifiableMap(outputBindingMap);
}
//Modified by Gianpaolo Coro
private void parseClass() {
if (!algorithmClass.isAnnotationPresent(Algorithm.class)) {
throw new RuntimeException("Class isn't annotated with an Algorithm annotation");
}
boolean validContructor = false;
try {
Constructor defaultConstructor = algorithmClass.getConstructor(new Class[0]);
validContructor = (defaultConstructor.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC;
} catch (NoSuchMethodException ex) {
// inherit error message on fall through...
} catch (SecurityException ex) {
throw new RuntimeException("Current security policy limits use of reflection, error introspecting " + algorithmClass.getName());
}
if (!validContructor) {
throw new RuntimeException("Classes with Algorithm annotation require public no-arg constructor, error introspecting " + algorithmClass.getName());
}
AlgorithmDescriptor.Builder<?> algorithmBuilder = null;
Algorithm algorithm = algorithmClass.getAnnotation(Algorithm.class);
//MODIFICATIONS BY G CORO TO ACCOUNT FOR METHODS ORDER
List<Method> sortedMethods = new ArrayList<Method>();
List<Integer> methodsIdxs = new ArrayList<Integer>();
try{
for (Method m:algorithmClass.getDeclaredMethods()){
//System.out.println("Javaassist Analysing method "+m.getName());
ClassPool pool = ClassPool.getDefault();
ClassClassPath ccpath = new ClassClassPath(m.getDeclaringClass());
pool.insertClassPath(ccpath);
//System.out.println("Javaassist searching for canonical name "+m.getDeclaringClass().getCanonicalName());
CtClass cc = pool.get(m.getDeclaringClass().getCanonicalName());
//System.out.println("Javaassist cc "+cc.getName());
CtMethod javassistMethod = cc.getDeclaredMethod(m.getName());
//System.out.println("Javaassist method "+javassistMethod.getName());
int linenumber = javassistMethod.getMethodInfo().getLineNumber(0);
//System.out.println("Javaassist line number "+linenumber);
int i=0;
for (Integer methodsIdx :methodsIdxs){
if (methodsIdx>linenumber){
break;
}
i++;
}
sortedMethods.add(i,m);
methodsIdxs.add(i,linenumber);
}
}catch(Exception e){
LOGGER.warn("error getting method order",e);
}
Method[] sMethods = sortedMethods.toArray(new Method[sortedMethods.size()]);
algorithmBuilder = AlgorithmDescriptor.builder(
algorithm.identifier().length() > 0 ?
algorithm.identifier() :
algorithmClass.getCanonicalName());
algorithmBuilder.
title(algorithm.title()).
abstrakt(algorithm.abstrakt()).
version(algorithm.version()).
storeSupported(algorithm.storeSupported()).
statusSupported(algorithm.statusSupported());
parseElements(sMethods,
INPUT_METHOD_PARSERS,
OUTPUT_METHOD_PARSERS);
parseElements(algorithmClass.getDeclaredFields(),
INPUT_FIELD_PARSERS,
OUTPUT_FIELD_PARSERS);
for (Method method : sMethods) {
if (method.isAnnotationPresent(PROCESS_PARSER.getSupportedAnnotation())) {
ExecuteMethodBinding executeMethodBinding = PROCESS_PARSER.parse(method);
if (executeMethodBinding != null) {
if (this.executeMethodBinding != null) {
// we need to error out here because ordering of getDeclaredMethods() or
// getMethods() is not guarenteed to be consistent, if it were consistent
// maybe we could ignore this state, but having an algorithm behave
// differently betweeen runtimes would be bad...
throw new RuntimeException("Multiple execute method bindings encountered for class " + getClass().getCanonicalName());
}
this.executeMethodBinding = executeMethodBinding;
}
}
}
if(this.executeMethodBinding == null) {
throw new RuntimeException("No execute method binding for class " + this.algorithmClass.getCanonicalName());
}
for (InputBinding<?,?> inputBinding : inputBindingMap.values()) {
algorithmBuilder.addInputDescriptor(inputBinding.getDescriptor());
}
for (OutputBinding<?,?> outputBinding : outputBindingMap.values()) {
algorithmBuilder.addOutputDescriptor(outputBinding.getDescriptor());
}
algorithmDescriptor = algorithmBuilder.build();
}
public AlgorithmDescriptor getAlgorithmDescriptor() {
return algorithmDescriptor;
}
public ExecuteMethodBinding getExecuteMethodBinding() {
return executeMethodBinding;
}
public Map<String, AnnotationBinding.InputBinding<?, ?>> getInputBindingMap() {
return inputBindingMap;
}
public Map<String, AnnotationBinding.OutputBinding<?, ?>> getOutputBindingMap() {
return outputBindingMap;
}
public <M extends AccessibleObject & Member> void parseElements(
M members[],
List<InputAnnotationParser<?,M,?>> inputParser,
List<OutputAnnotationParser<?,M,?>> outputParser) {
for (M member : members) {
for (OutputAnnotationParser<?,M,?> parser : outputParser) {
if (member.isAnnotationPresent(parser.getSupportedAnnotation())) {
OutputBinding<?,?> binding = parser.parse(member);
if (binding != null) {
outputBindingMap.put(binding.getDescriptor().getIdentifier(), binding);
}
}
}
for (InputAnnotationParser<?,M,?> parser : inputParser) {
if (member.isAnnotationPresent(parser.getSupportedAnnotation())) {
InputBinding<?,?> binding = parser.parse(member);
if (binding != null) {
inputBindingMap.put(binding.getDescriptor().getIdentifier(), binding);
}
}
}
}
}
}