/*************************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.Field; import java.util.HashMap; import java.util.List; import java.util.Map; import com.thoughtworks.go.config.ConfigCache; import com.thoughtworks.go.config.ConfigTag; import com.thoughtworks.go.config.registry.ConfigElementImplementationRegistry; import com.thoughtworks.go.config.ConfigInterface; import com.thoughtworks.go.config.ConfigSubtag; import com.thoughtworks.go.security.GoCipher; import com.thoughtworks.go.util.ConfigUtil; import org.jdom2.Element; import static com.thoughtworks.go.config.ConfigCache.annotationFor; import static com.thoughtworks.go.config.ConfigCache.isAnnotationPresent; import static com.thoughtworks.go.util.ExceptionUtils.bomb; public class GoConfigSubtagLoader { private static Map<Field, ConfigSubtag> isSubTags = new HashMap<>(); private final ConfigUtil configUtil = new ConfigUtil("magic"); private final Element e; private final Field field; private ConfigCache configCache; private final ConfigElementImplementationRegistry registry; private final ConfigReferenceElements configReferenceElements; public static boolean isSubtag(Field field) { return findSubTag(field) != null; } private static ConfigSubtag findSubTag(Field field) { if (!isSubTags.containsKey(field)) { isSubTags.put(field, field.getAnnotation(ConfigSubtag.class)); } ConfigSubtag configSubtag = isSubTags.get(field); return configSubtag; } public static GoConfigSubtagLoader subtagParser(Element e, Field field, ConfigCache configCache, final ConfigElementImplementationRegistry registry, ConfigReferenceElements configReferenceElements) { return new GoConfigSubtagLoader(e, field, configCache, registry, configReferenceElements); } private GoConfigSubtagLoader(Element e, Field field, ConfigCache configCache, final ConfigElementImplementationRegistry registry, ConfigReferenceElements configReferenceElements) { this.e = e; this.field = field; this.configCache = configCache; this.registry = registry; this.configReferenceElements = configReferenceElements; } public Object parse() { Class<?> type = findTypeOfField(); if (type == null) { return null; } ConfigTag tag = GoConfigClassLoader.configTag(type, configCache); if (configUtil.optionalAndMissingTag(e, tag, findSubTag(field).optional())) { return null; } return GoConfigClassLoader.classParser(configUtil.getChild(e, tag), type, configCache, new GoCipher(), registry, configReferenceElements).parse(); } private Class<?> findTypeOfField() { Class<?> type = field.getType(); if (isInterface(type)) { for (Element subElement : (List<Element>) e.getChildren()) { Class<?> concreteType = findConcreteTypeFrom(subElement, type); if (concreteType != null) { return concreteType; } } boolean optional = annotationFor(field, ConfigSubtag.class).optional(); if (optional) { return null; } throw bomb("Unable to find a tag of type '" + type.getSimpleName() + "' under element '" + e.getName() + "'"); } return field.getType(); } private Class<?> findConcreteTypeFrom(Element element, Class<?> interfaceType) { for (Class<?> implementation : registry.implementersOf(interfaceType)) { if (GoConfigClassLoader.compare(element, implementation, configCache)) { return implementation; } } return null; } private boolean isInterface(Class<?> aClass) { return isAnnotationPresent(aClass, ConfigInterface.class); } }