act.inject.param.CliContextParamLoader Maven / Gradle / Ivy
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.Act;
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 act.inject.DefaultValue;
import act.util.ActContext;
import org.osgl.$;
import org.osgl.exception.UnexpectedException;
import org.osgl.http.H;
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.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
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 methodMetaInfoHolder = new ThreadLocal();
private ConcurrentMap> optionLoaderRegistry = new ConcurrentHashMap>();
CliContextParamLoader(App app) {
super(app);
}
public CliContext.ParsingContext buildParsingContext(Class commander, Method method, CommandMethodMetaInfo methodMetaInfo) {
CliContext.ParsingContextBuilder.start();
ensureOptionLoaders(method, methodMetaInfo);
methodMetaInfoHolder.set(methodMetaInfo);
if (!Modifier.isStatic(method.getModifiers())) {
ParamValueLoader loader = findBeanLoader(commander);
classRegistry.putIfAbsent(commander, loader);
}
$.Var boolBag = $.var();
// create a pseudo ctx as we do not have one here
// the ctx is just a way to pass the method info
ActContext ctx = new ActContext.Base(Act.app()) {
@Override
public Base accept(H.Format fmt) {
return null;
}
@Override
public H.Format accept() {
return null;
}
@Override
public String methodPath() {
return null;
}
@Override
public Set paramKeys() {
return null;
}
@Override
public String paramVal(String key) {
return null;
}
@Override
public String[] paramVals(String key) {
return new String[0];
}
};
ctx.currentMethod(method);
ParamValueLoader[] loaders = findMethodParamLoaders(method, commander, ctx, boolBag);
methodRegistry.putIfAbsent(method, loaders);
methodValidationConstraintLookup.put(method, boolBag.get());
return CliContext.ParsingContextBuilder.finish();
}
public void preParseOptions(Method method, CommandMethodMetaInfo methodMetaInfo, CliContext context) {
List 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,
BeanSpec spec
) {
boolean isArray = spec.isArray();
String defVal = null;
DefaultValue defaultValue = spec.getAnnotation(DefaultValue.class);
if (null != defaultValue) {
defVal = defaultValue.value();
}
StringValueResolver resolver = findResolver(spec, isArray); //= isArray ? resolverManager.resolver(rawType.getComponentType(), spec) : resolverManager.resolver(rawType, spec);
Required required = spec.getAnnotation(Required.class);
Optional optional = null == required ? spec.getAnnotation(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(spec.rawType().getComponentType(), resolver) : new CliArgumentLoader(resolver, defVal);
}
private StringValueResolver findResolver(BeanSpec spec, boolean isArray) {
StringValueResolver resolver = findAnnotatedResolver(spec);
return null == resolver ? findImplicitResolver(spec, isArray) : resolver;
}
private StringValueResolver findAnnotatedResolver(BeanSpec spec) {
StringValueResolver resolver = findDirectAnnotatedResolver(spec);
return null == resolver ? findIndirectAnnotatedResolver(spec) : resolver;
}
private StringValueResolver findDirectAnnotatedResolver(BeanSpec spec) {
Resolve resolve = spec.getAnnotation(Resolve.class);
Class> rawType = spec.rawType();
if (null != resolve) {
Class extends StringValueResolver>[] resolvers = resolve.value();
for (Class extends StringValueResolver> resolverClass : resolvers) {
StringValueResolver resolver = injector.get(resolverClass);
Class> targetType = resolver.targetType();
boolean matches = rawType.isAssignableFrom(targetType);
if (!matches) {
Class> rawType2 = $.wrapperClassOf(rawType);
if (rawType != rawType2) {
matches = rawType2.isAssignableFrom(targetType);
}
}
if (matches) {
return resolver;
}
}
}
return null;
}
private StringValueResolver findIndirectAnnotatedResolver(BeanSpec spec) {
Annotation[] aa = spec.allAnnotations();
Class> rawType = spec.rawType();
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));
Class> targetType = resolver.targetType();
boolean matches = rawType.isAssignableFrom(targetType);
if (!matches) {
Class> rawType2 = $.wrapperClassOf(rawType);
if (rawType != rawType2) {
matches = rawType2.isAssignableFrom(targetType);
}
}
if (matches) {
return resolver;
}
}
}
}
return null;
}
private StringValueResolver findImplicitResolver(final BeanSpec spec, boolean isArray) {
StringValueResolver resolver = resolverManager.resolver(spec.rawType(), spec);
if (null != resolver) {
return resolver;
} else if (isArray) {
final BeanSpec compSpec = spec.componentSpec();
final StringValueResolver colResolver = resolverManager.collectionResolver(ArrayList.class, compSpec.rawType(), S.COMMON_SEP);
final boolean isPrimitive = $.isPrimitive(compSpec.rawType());
return new StringValueResolver() {
@Override
public Object resolve(String s) {
List list = colResolver.resolve(s);
int size = list.size();
final Class> compType = compSpec.rawType();
Object array = Array.newInstance(compType, size);
for (int i = 0; i < size; ++i) {
Object item = list.get(i);
if (isPrimitive) {
if (boolean.class == compType) {
Array.setBoolean(array, i, ((Boolean)item).booleanValue());
} else if (byte.class == compType) {
Array.setByte(array, i, ((Byte) item).byteValue());
} else if (char.class == compType) {
Array.setChar(array, i, ((Character) item).charValue());
} else if (double.class == compType) {
Array.setDouble(array, i, ((Double) item).doubleValue());
} else if (float.class == compType) {
Array.setFloat(array, i, ((Float) item).floatValue());
} else if (int.class == compType) {
Array.setInt(array, i, ((Integer) item).intValue());
} else if (long.class == compType) {
Array.setLong(array, i, ((Long) item).longValue());
} else if (short.class == compType) {
Array.setShort(array, i, ((Short) item).shortValue());
} else {
throw new UnexpectedException("Unknown primitive type");
}
} else {
Array.set(array, i, item);
}
}
return array;
}
};
} else {
return null;
}
}
@Override
protected String paramName(int i) {
return methodMetaInfoHolder.get().param(i).name();
}
private List ensureOptionLoaders(Method method, CommandMethodMetaInfo methodMetaInfo) {
List optionLoaders = optionLoaderRegistry.get(method);
if (null == optionLoaders) {
optionLoaders = findOptionLoaders(method, methodMetaInfo);
optionLoaderRegistry.put(method, optionLoaders);
}
return optionLoaders;
}
private List findOptionLoaders(Method method, CommandMethodMetaInfo methodMetaInfo) {
List optionLoaders = new ArrayList();
findParamOptionLoaders(method, methodMetaInfo, optionLoaders);
findFieldOptionLoaders(method.getDeclaringClass(), optionLoaders);
return optionLoaders;
}
private void findFieldOptionLoaders(Class c, List optionLoaders) {
if (injector.isProvided(c)) {
// No field injection for a provided host
return;
}
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);
boolean provided = injector.isProvided(spec);
ParamValueLoader loader = provided ? ProvidedValueLoader.get(spec, injector) : findContextSpecificLoader(bindName, spec);
if (loader instanceof OptionLoader) {
optionLoaders.add((OptionLoader) loader);
}
}
}
private void findParamOptionLoaders(Method m, CommandMethodMetaInfo methodMetaInfo, List 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);
if (loader instanceof OptionLoader) {
optionLoaders.add((OptionLoader) loader);
} else if (!$.isSimpleType(spec.rawType())) {
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy