/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.streamsets.datacollector.definition;
import com.google.common.collect.ImmutableMap;
import com.streamsets.datacollector.config.ModelDefinition;
import com.streamsets.datacollector.config.ModelType;
import com.streamsets.pipeline.api.ChooserValues;
import com.streamsets.pipeline.api.ListBeanModel;
import com.streamsets.pipeline.api.ConfigDef;
import com.streamsets.pipeline.api.FieldSelectorModel;
import com.streamsets.pipeline.api.PredicateModel;
import com.streamsets.pipeline.api.MultiValueChooserModel;
import com.streamsets.pipeline.api.ValueChooserModel;
import com.streamsets.pipeline.api.impl.ErrorMessage;
import com.streamsets.pipeline.api.impl.Utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class ModelDefinitionExtractor {
public abstract List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg);
public abstract ModelDefinition extract(String configPrefix, Field field, Object contextMsg);
private static final ModelDefinitionExtractor EXTRACTOR = new Extractor();
public static ModelDefinitionExtractor get() {
return EXTRACTOR;
}
static class Extractor extends ModelDefinitionExtractor {
private static final Map<Class<? extends Annotation>, ModelDefinitionExtractor> MODEL_EXTRACTOR =
ImmutableMap.<Class<? extends Annotation>, ModelDefinitionExtractor>builder()
.put(FieldSelectorModel.class, new FieldSelectorExtractor())
.put(ValueChooserModel.class, new ValueChooserExtractor())
.put(MultiValueChooserModel.class, new MultiValueChooserExtractor())
.put(PredicateModel.class, new PredicateExtractor())
.put(ListBeanModel.class, new ListBeanExtractor())
.build();
@Override
public List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = new ArrayList<>();
ConfigDef configAnnotation = field.getAnnotation(ConfigDef.class);
if (configAnnotation != null) {
if (configAnnotation.type() == ConfigDef.Type.MODEL) {
List<Annotation> modelAnnotations = new ArrayList<>();
for (Class<? extends Annotation> modelAnnotationClass : MODEL_EXTRACTOR.keySet()) {
Annotation modelAnnotation = field.getAnnotation(modelAnnotationClass);
if (modelAnnotation != null) {
modelAnnotations.add(modelAnnotation);
}
}
if (modelAnnotations.isEmpty()) {
errors.add(new ErrorMessage(DefinitionError.DEF_200, contextMsg));
}
if (modelAnnotations.size() > 1) {
errors.add(new ErrorMessage(DefinitionError.DEF_201, contextMsg, modelAnnotations));
}
if (modelAnnotations.size() > 0) {
Annotation modelAnnotation = modelAnnotations.get(0);
ModelDefinitionExtractor extractor = MODEL_EXTRACTOR.get(modelAnnotation.annotationType());
if (extractor == null) {
errors.add(new ErrorMessage(DefinitionError.DEF_202, contextMsg, modelAnnotation));
} else {
errors.addAll(extractor.validate(configPrefix, field, contextMsg));
}
}
}
}
return errors;
}
@Override
public ModelDefinition extract(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = validate(configPrefix, field, contextMsg);
if (errors.isEmpty()) {
ModelDefinition def = null;
ConfigDef configAnnotation = field.getAnnotation(ConfigDef.class);
if (configAnnotation != null) {
if (configAnnotation.type() == ConfigDef.Type.MODEL) {
Set<Annotation> modelAnnotations = new HashSet<>();
for (Class<? extends Annotation> modelAnnotationClass : MODEL_EXTRACTOR.keySet()) {
Annotation modelAnnotation = field.getAnnotation(modelAnnotationClass);
if (modelAnnotation != null) {
modelAnnotations.add(modelAnnotation);
}
}
Annotation modelAnnotation = modelAnnotations.iterator().next();
ModelDefinitionExtractor extractor = MODEL_EXTRACTOR.get(modelAnnotation.annotationType());
def = extractor.extract(configPrefix, field, contextMsg);
}
}
return def;
} else {
throw new IllegalArgumentException(Utils.format("Invalid ModelDefinition: {}", errors));
}
}
}
static class FieldSelectorExtractor extends ModelDefinitionExtractor {
@Override
public List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg) {
return new ArrayList<>();
}
@Override
public ModelDefinition extract(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = validate(configPrefix, field, contextMsg);
if (errors.isEmpty()) {
FieldSelectorModel fieldSelectorModel = field.getAnnotation(FieldSelectorModel.class);
ModelType modelType = (fieldSelectorModel.singleValued()) ? ModelType.FIELD_SELECTOR
: ModelType.FIELD_SELECTOR_MULTI_VALUE;
return new ModelDefinition(modelType, null, null, null, null, null);
} else {
throw new IllegalArgumentException(Utils.format("Invalid ModelDefinition: {}", errors));
}
}
}
static class ValueChooserExtractor extends ModelDefinitionExtractor {
@Override
public List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = new ArrayList<>();
try {
ValueChooserModel valueChooserModel = field.getAnnotation(ValueChooserModel.class);
ChooserValues values = valueChooserModel.value().newInstance();
} catch (Exception ex) {
errors.add(new ErrorMessage(DefinitionError.DEF_220, contextMsg, ex.toString()));
}
return errors;
}
@Override
public ModelDefinition extract(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = validate(configPrefix, field, contextMsg);
if (errors.isEmpty()) {
ValueChooserModel valueChooserModel = field.getAnnotation(ValueChooserModel.class);
try {
ChooserValues values = valueChooserModel.value().newInstance();
return new ModelDefinition(ModelType.VALUE_CHOOSER, values.getClass().getName(), values.getValues(),
values.getLabels(), null, null);
} catch (Exception ex) {
throw new RuntimeException(Utils.format("Unexpected exception: {}", ex.toString()), ex);
}
} else {
throw new IllegalArgumentException(Utils.format("Invalid ModelDefinition: {}", errors));
}
}
}
static class MultiValueChooserExtractor extends ModelDefinitionExtractor {
@Override
public List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = new ArrayList<>();
try {
MultiValueChooserModel multiValueChooserModel = field.getAnnotation(MultiValueChooserModel.class);
ChooserValues values = multiValueChooserModel.value().newInstance();
} catch (Exception ex) {
errors.add(new ErrorMessage(DefinitionError.DEF_220, contextMsg, ex.toString()));
}
return errors;
}
@Override
public ModelDefinition extract(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = validate(configPrefix, field, contextMsg);
if (errors.isEmpty()) {
MultiValueChooserModel multiValueChooserModel = field.getAnnotation(MultiValueChooserModel.class);
try {
ChooserValues values = multiValueChooserModel.value().newInstance();
return new ModelDefinition(ModelType.MULTI_VALUE_CHOOSER, values.getClass().getName(), values.getValues(),
values.getLabels(), null, null);
} catch (Exception ex) {
throw new RuntimeException(Utils.format("Unexpected exception: {}", ex.toString()), ex);
}
} else {
throw new IllegalArgumentException(Utils.format("Invalid ModelDefinition: {}", errors));
}
}
}
static class PredicateExtractor extends ModelDefinitionExtractor {
@Override
public List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg) {
return new ArrayList<>();
}
@Override
public ModelDefinition extract(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = validate(configPrefix, field, contextMsg);
if (errors.isEmpty()) {
return new ModelDefinition(ModelType.PREDICATE, null, null, null, null, null);
} else {
throw new IllegalArgumentException(Utils.format("Invalid ModelDefinition: {}", errors));
}
}
}
static class ListBeanExtractor extends ModelDefinitionExtractor {
@Override
public List<ErrorMessage> validate(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = new ArrayList<>();
if (!List.class.isAssignableFrom(field.getType())) {
errors.add(new ErrorMessage(DefinitionError.DEF_230, contextMsg));
} else {
Class listBeanClass = (Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0];
errors.addAll(ConfigDefinitionExtractor.get().validateComplexField("", listBeanClass,
Collections.<String>emptyList(),contextMsg));
}
return errors;
}
@Override
public ModelDefinition extract(String configPrefix, Field field, Object contextMsg) {
List<ErrorMessage> errors = validate(configPrefix, field, contextMsg);
if (errors.isEmpty()) {
Class listBeanClass = (Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0];
return new ModelDefinition(ModelType.LIST_BEAN, null, null, null, listBeanClass,
ConfigDefinitionExtractor.get().extract("", listBeanClass,
Collections.<String>emptyList(), contextMsg));
} else {
throw new IllegalArgumentException(Utils.format("Invalid ModelDefinition: {}", errors));
}
}
}
}