package act.inject.param; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import act.app.ActionContext; import act.app.App; import act.cli.CliContext; import act.cli.Optional; import act.cli.Required; import act.cli.meta.CommandMethodMetaInfo; import act.cli.util.CommandLineParser; import org.osgl.$; import org.osgl.inject.BeanSpec; import org.osgl.inject.util.AnnotationUtil; import org.osgl.mvc.annotation.Resolve; import org.osgl.util.S; import org.osgl.util.StringValueResolver; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Responsible for loading param value for {@link ActionContext} */ public class CliContextParamLoader extends ParamValueLoaderService { private final static transient ThreadLocal<CommandMethodMetaInfo> methodMetaInfoHolder = new ThreadLocal<CommandMethodMetaInfo>(); private ConcurrentMap<Method, List<OptionLoader>> optionLoaderRegistry = new ConcurrentHashMap<Method, List<OptionLoader>>(); CliContextParamLoader(App app) { super(app); } public CliContext.ParsingContext buildParsingContext(Class commander, Method method, CommandMethodMetaInfo methodMetaInfo) { CliContext.ParsingContextBuilder.start(); ensureOptionLoaders(method, methodMetaInfo); methodMetaInfoHolder.set(methodMetaInfo); ParamValueLoader loader = findBeanLoader(commander); classRegistry.putIfAbsent(commander, loader); $.Var<Boolean> boolBag = $.var(); ParamValueLoader[] loaders = findMethodParamLoaders(method, commander, boolBag); methodRegistry.putIfAbsent(method, loaders); methodValidationConstraintLookup.put(method, boolBag.get()); return CliContext.ParsingContextBuilder.finish(); } public void preParseOptions(Method method, CommandMethodMetaInfo methodMetaInfo, CliContext context) { List<OptionLoader> optionLoaders = ensureOptionLoaders(method, methodMetaInfo); CommandLineParser commandLineParser = context.commandLine(); boolean argumentAsOption = false; if (1 == optionLoaders.size()) { OptionLoader loader = optionLoaders.get(0); if (loader.required) { String theOptionVal = commandLineParser.argumentAsOption(); if (null != theOptionVal) { argumentAsOption = true; context.parsingContext().foundRequired(loader.requiredGroup); context.param(loader.bindName, theOptionVal); } } } if (!argumentAsOption) { for (OptionLoader loader : optionLoaders) { String bindName = loader.bindName; String value = commandLineParser.getString(loader.lead1, loader.lead2); if (S.notBlank(value)) { if (loader.required) { context.parsingContext().foundRequired(loader.requiredGroup); } context.param(bindName, value); } } } context.parsingContext().raiseExceptionIfThereAreMissingOptions(context); } @Override protected ParamValueLoader findContextSpecificLoader( String bindName, Class rawType, BeanSpec spec, Type type, Annotation[] annotations ) { boolean isArray = rawType.isArray(); StringValueResolver resolver = findResolver(spec, rawType, isArray); //= isArray ? resolverManager.resolver(rawType.getComponentType(), spec) : resolverManager.resolver(rawType, spec); Required required = filter(annotations, Required.class); Optional optional = null == required ? filter(annotations, Optional.class) : null; if (null != required) { return new OptionLoader(bindName, required, resolver, spec); } else if (null != optional) { return new OptionLoader(bindName, optional, resolver, spec); } return isArray ? new CliVarArgumentLoader(rawType.getComponentType(), resolver) : new CliArgumentLoader(resolver); } private StringValueResolver findResolver(BeanSpec spec, Class rawType, boolean isArray) { StringValueResolver resolver = findAnnotatedResolver(spec, rawType); return null == resolver ? findImplictResolver(spec, rawType, isArray) : resolver; } private StringValueResolver findAnnotatedResolver(BeanSpec spec, Class rawType) { StringValueResolver resolver = findDirectAnnotatedResolver(spec, rawType); return null == resolver ? findIndirectAnnotatedResolver(spec, rawType) : resolver; } private StringValueResolver findDirectAnnotatedResolver(BeanSpec spec, Class rawType) { Resolve resolve = spec.getAnnotation(Resolve.class); if (null != resolve) { Class<? extends StringValueResolver>[] resolvers = resolve.value(); for (Class<? extends StringValueResolver> resolverClass : resolvers) { StringValueResolver resolver = injector.get(resolverClass); if (rawType.isAssignableFrom(resolver.targetType())) { return resolver; } } } return null; } private StringValueResolver findIndirectAnnotatedResolver(BeanSpec spec, Class rawType) { Annotation[] aa = spec.allAnnotations(); for (Annotation a : aa) { Resolve resolve = AnnotationUtil.tagAnnotation(a, Resolve.class); if (null != resolve) { Class<? extends StringValueResolver>[] resolvers = resolve.value(); for (Class<? extends StringValueResolver> resolverClass : resolvers) { StringValueResolver resolver = injector.get(resolverClass); resolver.attributes($.evaluate(a)); if (rawType.isAssignableFrom(resolver.targetType())) { return resolver; } } } } return null; } private StringValueResolver findImplictResolver(BeanSpec spec, Class rawType, boolean isArray) { return isArray ? resolverManager.resolver(rawType.getComponentType(), spec) : resolverManager.resolver(rawType, spec); } @Override protected String paramName(int i) { return methodMetaInfoHolder.get().param(i).name(); } private List<OptionLoader> ensureOptionLoaders(Method method, CommandMethodMetaInfo methodMetaInfo) { List<OptionLoader> optionLoaders = optionLoaderRegistry.get(method); if (null == optionLoaders) { optionLoaders = findOptionLoaders(method, methodMetaInfo); optionLoaderRegistry.put(method, optionLoaders); } return optionLoaders; } private List<OptionLoader> findOptionLoaders(Method method, CommandMethodMetaInfo methodMetaInfo) { List<OptionLoader> optionLoaders = new ArrayList<OptionLoader>(); findFieldOptionLoaders(method.getDeclaringClass(), optionLoaders); findParamOptionLoaders(method, methodMetaInfo, optionLoaders); return optionLoaders; } private void findFieldOptionLoaders(Class c, List<OptionLoader> optionLoaders) { for (Field field : $.fieldsOf(c, true)) { Type type = field.getGenericType(); Annotation[] annotations = field.getAnnotations(); String bindName = bindName(annotations, field.getName()); BeanSpec spec = BeanSpec.of(type, annotations, bindName, injector); ParamValueLoader loader = findContextSpecificLoader(bindName, field.getDeclaringClass(), spec, type, annotations); if (loader instanceof OptionLoader) { optionLoaders.add((OptionLoader) loader); } } } private void findParamOptionLoaders(Method m, CommandMethodMetaInfo methodMetaInfo, List<OptionLoader> optionLoaders) { Type[] types = m.getGenericParameterTypes(); int len = types.length; if (len == 0) { return; } Annotation[][] allAnnotations = m.getParameterAnnotations(); for (int i = len - 1; i >= 0; --i) { Type type = types[i]; Annotation[] annotations = allAnnotations[i]; BeanSpec spec = BeanSpec.of(type, annotations, null, injector); String bindName = tryFindBindName(annotations, spec.name()); if (null == bindName) { bindName = methodMetaInfo.param(i).name(); } ParamValueLoader loader = findContextSpecificLoader(bindName, spec.rawType(), spec, type, annotations); if (loader instanceof OptionLoader) { optionLoaders.add((OptionLoader) loader); } else if (!$.isSimpleType(spec.rawType())) { } } } }