325 lines
16 KiB
Java
325 lines
16 KiB
Java
|
/**
|
|||
|
* 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.annotation.Annotation;
|
|||
|
import java.lang.reflect.AccessibleObject;
|
|||
|
import java.lang.reflect.Field;
|
|||
|
import java.lang.reflect.Member;
|
|||
|
import java.lang.reflect.Method;
|
|||
|
import java.lang.reflect.Type;
|
|||
|
import java.util.ArrayList;
|
|||
|
import java.util.Arrays;
|
|||
|
import java.util.List;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.InputBinding;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.InputFieldBinding;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.InputMethodBinding;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputBinding;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputFieldBinding;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputMethodBinding;
|
|||
|
import org.n52.wps.algorithm.annotation.AnnotationBinding.ExecuteMethodBinding;
|
|||
|
import org.n52.wps.algorithm.descriptor.BoundDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.ComplexDataInputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.ComplexDataOutputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.InputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.OutputDescriptor;
|
|||
|
import org.n52.wps.algorithm.util.ClassUtil;
|
|||
|
import org.n52.wps.io.BasicXMLTypeFactory;
|
|||
|
import org.n52.wps.io.data.ILiteralData;
|
|||
|
import org.slf4j.Logger;
|
|||
|
import org.slf4j.LoggerFactory;
|
|||
|
|
|||
|
/**
|
|||
|
*
|
|||
|
* @author tkunicki
|
|||
|
*/
|
|||
|
public abstract class AnnotationParser<A extends Annotation, M extends AccessibleObject & Member, B extends AnnotationBinding<M>> {
|
|||
|
|
|||
|
public final static Logger LOGGER = LoggerFactory.getLogger(AnnotationParser.class);
|
|||
|
|
|||
|
public B parse(M member) {
|
|||
|
A annotation = member.getAnnotation(getSupportedAnnotation());
|
|||
|
return annotation == null ? null : parse(annotation, member);
|
|||
|
}
|
|||
|
|
|||
|
public abstract B parse(A annotation, M member);
|
|||
|
|
|||
|
public abstract Class<? extends A> getSupportedAnnotation();
|
|||
|
|
|||
|
public static class ExecuteAnnotationParser extends AnnotationParser<Execute, Method, ExecuteMethodBinding> {
|
|||
|
|
|||
|
@Override
|
|||
|
public ExecuteMethodBinding parse(Execute annotation, Method member) {
|
|||
|
ExecuteMethodBinding annotationBinding = new ExecuteMethodBinding(member);
|
|||
|
return annotationBinding.validate() ? annotationBinding : null;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends Execute> getSupportedAnnotation() {
|
|||
|
return Execute.class;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public abstract static class DataAnnotationParser<A extends Annotation, M extends AccessibleObject & Member, B extends AnnotationBinding.DataBinding<M, ? extends BoundDescriptor>>
|
|||
|
extends AnnotationParser<A,M,B> {
|
|||
|
protected abstract B createBinding(M member);
|
|||
|
}
|
|||
|
|
|||
|
public abstract static class InputAnnotationParser<A extends Annotation, M extends AccessibleObject & Member, B extends AnnotationBinding.InputBinding<M, ? extends InputDescriptor>>
|
|||
|
extends DataAnnotationParser<A,M,B> {}
|
|||
|
|
|||
|
public abstract static class OutputAnnotationParser<A extends Annotation, M extends AccessibleObject & Member, B extends AnnotationBinding.OutputBinding<M, ? extends OutputDescriptor>>
|
|||
|
extends DataAnnotationParser<A,M,B> {}
|
|||
|
|
|||
|
public abstract static class LiteralDataInputAnnotationParser<M extends AccessibleObject & Member, B extends AnnotationBinding.InputBinding<M, LiteralDataInputDescriptor>>
|
|||
|
extends InputAnnotationParser<LiteralDataInput, M, B> {
|
|||
|
|
|||
|
@Override
|
|||
|
public B parse(LiteralDataInput annotation, M member) {
|
|||
|
|
|||
|
B annotatedBinding = createBinding(member);
|
|||
|
// auto generate binding if it's not explicitly declared
|
|||
|
Type payloadType = annotatedBinding.getPayloadType();
|
|||
|
Class<? extends ILiteralData> binding = annotation.binding();
|
|||
|
if (binding == null || ILiteralData.class.equals(binding)) {
|
|||
|
if (payloadType instanceof Class<?>) {
|
|||
|
binding = BasicXMLTypeFactory.getBindingForPayloadType((Class<?>) payloadType);
|
|||
|
if (binding == null) {
|
|||
|
LOGGER.error("Unable to locate binding class for {}; binding not found.", payloadType);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (annotatedBinding.isMemberTypeList()) {
|
|||
|
LOGGER.error("Unable to determine binding class for {}; List must be parameterized with a type matching a known binding payload to use auto-binding.", payloadType);
|
|||
|
} else {
|
|||
|
LOGGER.error("Unable to determine binding class for {}; type must fully resolved to use auto-binding", payloadType);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
String[] allowedValues = annotation.allowedValues();
|
|||
|
String defaultValue = annotation.defaultValue();
|
|||
|
int maxOccurs = annotation.maxOccurs();
|
|||
|
// If InputType is enum
|
|||
|
// 1) generate allowedValues if not explicitly declared
|
|||
|
// 2) validate allowedValues if explicitly declared
|
|||
|
// 3) validate defaultValue if declared
|
|||
|
// 4) check for special ENUM_COUNT maxOccurs flag
|
|||
|
Type inputType = annotatedBinding.getType();
|
|||
|
if (annotatedBinding.isTypeEnum()) {
|
|||
|
Class<? extends Enum> inputEnumClass = (Class<? extends Enum>) inputType;
|
|||
|
// validate contents of allowed values maps to enum
|
|||
|
if (allowedValues.length > 0) {
|
|||
|
List<String> invalidValues = new ArrayList<String>();
|
|||
|
for (String value : allowedValues) {
|
|||
|
try {
|
|||
|
Enum.valueOf(inputEnumClass, value);
|
|||
|
} catch (IllegalArgumentException e) {
|
|||
|
invalidValues.add(value);
|
|||
|
LOGGER.warn("Invalid allowed value \"{}\" specified for for enumerated input type {}", value, inputType);
|
|||
|
}
|
|||
|
}
|
|||
|
if (invalidValues.size() > 0) {
|
|||
|
List<String> updatedValues = new ArrayList<String>(Arrays.asList(allowedValues));
|
|||
|
updatedValues.removeAll(invalidValues);
|
|||
|
allowedValues = updatedValues.toArray(new String[0]);
|
|||
|
}
|
|||
|
}
|
|||
|
// if list is empty, populated with values from enum
|
|||
|
if (allowedValues.length == 0) {
|
|||
|
allowedValues = ClassUtil.convertEnumToStringArray(inputEnumClass);
|
|||
|
}
|
|||
|
if (defaultValue.length() > 0) {
|
|||
|
try {
|
|||
|
Enum.valueOf(inputEnumClass, defaultValue);
|
|||
|
} catch (IllegalArgumentException e) {
|
|||
|
LOGGER.warn("Invalid default value \"{}\" specified for for enumerated input type {}, ignoring.", defaultValue, inputType);
|
|||
|
defaultValue = "";
|
|||
|
}
|
|||
|
}
|
|||
|
if (maxOccurs == LiteralDataInput.ENUM_COUNT) {
|
|||
|
maxOccurs = inputEnumClass.getEnumConstants().length;
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (maxOccurs == LiteralDataInput.ENUM_COUNT) {
|
|||
|
maxOccurs = 1;
|
|||
|
LOGGER.warn("Invalid maxOccurs \"ENUM_COUNT\" specified for for input type {}, setting maxOccurs to {}", inputType, maxOccurs);
|
|||
|
}
|
|||
|
}
|
|||
|
if (binding != null) {
|
|||
|
LiteralDataInputDescriptor descriptor =
|
|||
|
LiteralDataInputDescriptor.builder(annotation.identifier(), binding).
|
|||
|
title(annotation.title()).
|
|||
|
abstrakt(annotation.abstrakt()).
|
|||
|
minOccurs(annotation.minOccurs()).
|
|||
|
maxOccurs(maxOccurs).
|
|||
|
defaultValue(defaultValue).
|
|||
|
allowedValues(allowedValues).
|
|||
|
build();
|
|||
|
annotatedBinding.setDescriptor(descriptor);
|
|||
|
} else {
|
|||
|
LOGGER.error("Unable to generate binding for input identifier \"{}\"", annotation.identifier());
|
|||
|
}
|
|||
|
return annotatedBinding.validate() ? annotatedBinding : null;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends LiteralDataInput> getSupportedAnnotation() {
|
|||
|
return LiteralDataInput.class;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public abstract static class LiteralDataOutputAnnotationParser<M extends AccessibleObject & Member, B extends AnnotationBinding.OutputBinding<M, LiteralDataOutputDescriptor>>
|
|||
|
extends OutputAnnotationParser<LiteralDataOutput, M, B> {
|
|||
|
|
|||
|
@Override
|
|||
|
public B parse(LiteralDataOutput annotation, M member) {
|
|||
|
B annotatedBinding = createBinding(member);
|
|||
|
// auto generate binding if it's not explicitly declared
|
|||
|
Type payloadType = annotatedBinding.getPayloadType();
|
|||
|
Class<? extends ILiteralData> binding = annotation.binding();
|
|||
|
if (binding == null || ILiteralData.class.equals(binding)) {
|
|||
|
if (payloadType instanceof Class<?>) {
|
|||
|
binding = BasicXMLTypeFactory.getBindingForPayloadType((Class<?>) payloadType);
|
|||
|
if (binding == null) {
|
|||
|
LOGGER.error("Unable to locate binding class for {}; binding not found.", payloadType);
|
|||
|
}
|
|||
|
} else {
|
|||
|
LOGGER.error("Unable to determine binding class for {}; type must fully resolved to use auto-binding", payloadType);
|
|||
|
}
|
|||
|
}
|
|||
|
if (binding != null) {
|
|||
|
LiteralDataOutputDescriptor descriptor =
|
|||
|
LiteralDataOutputDescriptor.builder(annotation.identifier(), binding).
|
|||
|
title(annotation.title()).
|
|||
|
abstrakt(annotation.abstrakt()).
|
|||
|
build();
|
|||
|
annotatedBinding.setDescriptor(descriptor);
|
|||
|
} else {
|
|||
|
LOGGER.error("Unable to generate binding for output identifier \"{}\"", annotation.identifier());
|
|||
|
}
|
|||
|
return annotatedBinding.validate() ? annotatedBinding : null;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends LiteralDataOutput> getSupportedAnnotation() {
|
|||
|
return LiteralDataOutput.class;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static class ComplexDataInputAnnotationParser<M extends AccessibleObject & Member, B extends AnnotationBinding.InputBinding<M, ComplexDataInputDescriptor>>
|
|||
|
extends InputAnnotationParser<ComplexDataInput, M, B> {
|
|||
|
|
|||
|
@Override
|
|||
|
public B parse(ComplexDataInput annotation, M member) {
|
|||
|
B annotatedBinding = createBinding(member);
|
|||
|
ComplexDataInputDescriptor descriptor =
|
|||
|
ComplexDataInputDescriptor.builder(annotation.identifier(), annotation.binding()).
|
|||
|
title(annotation.title()).
|
|||
|
abstrakt(annotation.abstrakt()).
|
|||
|
minOccurs(annotation.minOccurs()).
|
|||
|
maxOccurs(annotation.maxOccurs()).
|
|||
|
maximumMegaBytes(annotation.maximumMegaBytes()).
|
|||
|
build();
|
|||
|
annotatedBinding.setDescriptor(descriptor);
|
|||
|
return annotatedBinding.validate() ? annotatedBinding : null;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends ComplexDataInput> getSupportedAnnotation() {
|
|||
|
return ComplexDataInput.class;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
protected B createBinding(M member) {
|
|||
|
throw new UnsupportedOperationException("Not supported yet.");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public abstract static class ComplexDataOutputAnnotationParser<M extends AccessibleObject & Member, B extends AnnotationBinding.OutputBinding<M, ComplexDataOutputDescriptor>>
|
|||
|
extends OutputAnnotationParser<ComplexDataOutput, M, B> {
|
|||
|
|
|||
|
@Override
|
|||
|
public B parse (ComplexDataOutput annotation, M member) {
|
|||
|
B annotatedBinding = createBinding(member);
|
|||
|
ComplexDataOutputDescriptor descriptor =
|
|||
|
ComplexDataOutputDescriptor.builder(annotation.identifier(), annotation.binding()).
|
|||
|
title(annotation.title()).
|
|||
|
abstrakt(annotation.abstrakt()).
|
|||
|
build();
|
|||
|
annotatedBinding.setDescriptor(descriptor);
|
|||
|
return annotatedBinding.validate() ? annotatedBinding : null;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends ComplexDataOutput> getSupportedAnnotation() {
|
|||
|
return ComplexDataOutput.class;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static class LiteralDataInputFieldAnnotationParser extends LiteralDataInputAnnotationParser<Field, AnnotationBinding.InputBinding<Field, LiteralDataInputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected InputBinding<Field, LiteralDataInputDescriptor> createBinding(Field member) {
|
|||
|
return new InputFieldBinding<LiteralDataInputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
public static class LiteralDataOutputFieldAnnotationParser extends LiteralDataOutputAnnotationParser<Field, AnnotationBinding.OutputBinding<Field, LiteralDataOutputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected OutputBinding<Field, LiteralDataOutputDescriptor> createBinding(Field member) {
|
|||
|
return new OutputFieldBinding<LiteralDataOutputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
public static class ComplexDataInputFieldAnnotationParser extends ComplexDataInputAnnotationParser<Field, AnnotationBinding.InputBinding<Field, ComplexDataInputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected InputBinding<Field, ComplexDataInputDescriptor> createBinding(Field member) {
|
|||
|
return new InputFieldBinding<ComplexDataInputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
public static class ComplexDataOutputFieldAnnotationParser extends ComplexDataOutputAnnotationParser<Field, AnnotationBinding.OutputBinding<Field, ComplexDataOutputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected OutputBinding<Field, ComplexDataOutputDescriptor> createBinding(Field member) {
|
|||
|
return new OutputFieldBinding<ComplexDataOutputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static class LiteralDataInputMethodAnnotationParser extends LiteralDataInputAnnotationParser<Method, AnnotationBinding.InputBinding<Method, LiteralDataInputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected InputBinding<Method, LiteralDataInputDescriptor> createBinding(Method member) {
|
|||
|
return new InputMethodBinding<LiteralDataInputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
public static class LiteralDataOutputMethodAnnotationParser extends LiteralDataOutputAnnotationParser<Method, AnnotationBinding.OutputBinding<Method, LiteralDataOutputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected OutputBinding<Method, LiteralDataOutputDescriptor> createBinding(Method member) {
|
|||
|
return new OutputMethodBinding<LiteralDataOutputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
public static class ComplexDataInputMethodAnnotationParser extends ComplexDataInputAnnotationParser<Method, AnnotationBinding.InputBinding<Method, ComplexDataInputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected InputBinding<Method, ComplexDataInputDescriptor> createBinding(Method member) {
|
|||
|
return new InputMethodBinding<ComplexDataInputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
public static class ComplexDataOutputMethodAnnotationParser extends ComplexDataOutputAnnotationParser<Method, AnnotationBinding.OutputBinding<Method, ComplexDataOutputDescriptor>> {
|
|||
|
@Override
|
|||
|
protected OutputBinding<Method, ComplexDataOutputDescriptor> createBinding(Method member) {
|
|||
|
return new OutputMethodBinding<ComplexDataOutputDescriptor>(member);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|