/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.config.parser; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import com.thoughtworks.go.config.ConfigAttributeValue; import com.thoughtworks.go.config.ConfigCache; import com.thoughtworks.go.config.ConfigReferenceElement; import com.thoughtworks.go.config.ConfigSubtag; import com.thoughtworks.go.config.ConfigValue; import com.thoughtworks.go.config.registry.ConfigElementImplementationRegistry; import com.thoughtworks.go.security.GoCipher; import org.jdom2.Element; import org.springframework.beans.SimpleTypeConverter; import org.springframework.beans.TypeMismatchException; import static com.thoughtworks.go.config.ConfigCache.isAnnotationPresent; import static com.thoughtworks.go.config.parser.GoConfigAttributeLoader.attributeParser; import static com.thoughtworks.go.config.parser.GoConfigAttributeLoader.isAttribute; import static com.thoughtworks.go.config.parser.GoConfigSubtagLoader.isSubtag; import static com.thoughtworks.go.config.parser.GoConfigSubtagLoader.subtagParser; import static com.thoughtworks.go.util.ExceptionUtils.bomb; import static java.text.MessageFormat.format; public class GoConfigFieldLoader<T> { private static Map<Field, Boolean> implicts = new HashMap<>(); private static final SimpleTypeConverter typeConverter = new GoConfigFieldTypeConverter(); private final Element e; private final T instance; private final Field field; private ConfigCache configCache; private final ConfigReferenceElements configReferenceElements; private final ConfigElementImplementationRegistry registry; public static <T> GoConfigFieldLoader<T> fieldParser(Element e, T instance, Field field, ConfigCache configCache, final ConfigElementImplementationRegistry registry, ConfigReferenceElements configReferenceElements) { return new GoConfigFieldLoader<>(e, instance, field, configCache, registry, configReferenceElements); } private GoConfigFieldLoader(Element e, T instance, Field field, ConfigCache configCache, final ConfigElementImplementationRegistry registry, ConfigReferenceElements configReferenceElements) { this.e = e; this.instance = instance; this.field = field; this.configCache = configCache; this.configReferenceElements = configReferenceElements; field.setAccessible(true); this.registry = registry; } public void parse() { if (isImplicitCollection()) { Object val = GoConfigClassLoader.classParser(e, field.getType(), configCache, new GoCipher(), registry, configReferenceElements).parseImplicitCollection(); setValue(val); } else if (isSubtag(field)) { Object val = subtagParser(e, field, configCache, registry, configReferenceElements).parse(); setValue(val); } else if (isAttribute(field)) { Object val = attributeParser(e, field).parse(defaultValue()); setValue(val); } else if (isConfigValue()) { Object val = e.getText(); setValue(val); } else if (isAnnotationPresent(field, ConfigReferenceElement.class)) { ConfigReferenceElement referenceField = field.getAnnotation(ConfigReferenceElement.class); String refId = e.getAttribute(referenceField.referenceAttribute()).getValue(); Object referredObject = configReferenceElements.get(referenceField.referenceCollection(), refId); setValue(referredObject); } } private boolean isImplicitCollection() { if (!implicts.containsKey(field)) { implicts.put(field, ConfigCache.isAnnotationPresent(field, ConfigSubtag.class) && GoConfigClassLoader.isImplicitCollection(field.getType(), configCache)); } return implicts.get(field); } private void setValue(Object val) { try { ConfigAttributeValue configAttributeValue = field.getType().getAnnotation(ConfigAttributeValue.class); if (configAttributeValue != null) { if (val != null || configAttributeValue.createForNull()) { Constructor<?> constructor = field.getType().getConstructor(String.class); field.set(instance, constructor.newInstance(new Object[]{val})); } } else if (val != null) { Object convertedValue = typeConverter.convertIfNecessary(val, field.getType()); field.set(instance, convertedValue); } } catch (IllegalAccessException e) { throw bomb("Error setting configField: " + field.getName(), e); } catch (TypeMismatchException e) { final String message = format("Could not set value [{0}] on Field [{1}] of type [{2}] ", val, field.getName(), field.getType()); throw bomb(message, e); } catch (NoSuchMethodException e) { throw bomb("Error setting configField: " + field.getName() + " as " + field.getType(), e); } catch (InstantiationException e) { throw bomb("Error creating configAttribute: " + field.getName() + " as " + field.getType(), e); } catch (InvocationTargetException e) { throw bomb("Error creating configAttribute: " + field.getName() + " as " + field.getType(), e); } } private Object defaultValue() { try { return field.get(instance); } catch (IllegalAccessException e) { throw bomb("Error getting configField: " + field.getName(), e); } } public boolean isConfigValue() { return isAnnotationPresent(field, ConfigValue.class); } }