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);
|
||
}
|
||
}
|
||
}
|