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