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

325 lines
16 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.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);
}
}
}