package act.cli.meta;
/*-
* #%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.Destroyable;
import act.app.AppClassLoader;
import act.asm.Type;
import act.cli.view.CliView;
import act.handler.CliHandler;
import act.sys.meta.InvokeType;
import act.sys.meta.ReturnTypeInfo;
import act.util.DestroyableBase;
import act.util.PropertySpec;
import org.osgl.$;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.List;
import java.util.Set;
/**
* Stores the command method meta info including
* <ul>
* <li>method name</li>
* <li>method invocation type</li>
* <li>return type</li>
* <li>param info and associated annotation info</li>
* </ul>
*/
@ApplicationScoped
public class CommandMethodMetaInfo extends DestroyableBase {
private String methodName;
private String commandName;
private String helpMsg;
private InvokeType invokeType;
private CommanderClassMetaInfo clsInfo;
private PropertySpec.MetaInfo propertySpec;
private C.List<CommandParamMetaInfo> params = C.newList();
private ReturnTypeInfo returnType;
private Set<String> optionLeads = C.newSet();
private CliView view = CliView.TO_STRING;
private Act.Mode mode = Act.Mode.PROD;
private int ctxParamCnt = -1;
@Inject
public CommandMethodMetaInfo(CommanderClassMetaInfo clsInfo) {
this.clsInfo = $.NPE(clsInfo);
}
public CommanderClassMetaInfo classInfo() {
return clsInfo;
}
public CommandMethodMetaInfo methodName(String name) {
this.methodName = $.NPE(name);
return this;
}
public String methodName() {
return methodName;
}
public CommandMethodMetaInfo view(CliView view) {
this.view = $.notNull(view);
return this;
}
public CliView view() {
return view;
}
public CommandMethodMetaInfo commandName(String name) {
commandName = $.NPE(name);
return this;
}
public String commandName() {
return commandName;
}
public String fullName() {
return S.newBuffer(clsInfo.className()).append(".").append(methodName()).toString();
}
public CommandMethodMetaInfo helpMsg(String msg) {
helpMsg = msg;
return this;
}
public String helpMsg() {
return null == helpMsg ? "" : helpMsg;
}
/**
* Returns {@link CliHandler#options()}
* @return options list
* @see CliHandler#options()
*/
public List<$.T2<String, String>> options(CommanderClassMetaInfo classMetaInfo, AppClassLoader classLoader) {
List<$.T2<String, String>> retVal = C.newList();
for (CommandParamMetaInfo param : params) {
OptionAnnoInfoBase opt = param.optionInfo();
if (null != opt) {
retVal.add($.T2(opt.leads(), opt.help()));
}
}
for (OptionAnnoInfoBase opt : classMetaInfo.fieldOptionAnnoInfoList(classLoader)) {
retVal.add($.T2(opt.leads(), opt.help()));
}
return retVal;
}
/**
* Returns {@link act.handler.CliHandler#commandLine()}
* @return the command line
* @see act.handler.CliHandler#commandLine()
*/
public $.T2<String, String> commandLine(CommanderClassMetaInfo classMetaInfo, AppClassLoader classLoader) {
boolean hasOptions = classMetaInfo.hasOption(classLoader);
String firstArg = null;
boolean hasMoreArgs = false;
for (CommandParamMetaInfo param : params) {
if (param.optionInfo() != null) {
hasOptions = true;
} else {
if (firstArg == null) {
firstArg = param.name();
} else {
hasMoreArgs = true;
}
}
}
for (FieldOptionAnnoInfo fieldOptionAnnoInfo : classMetaInfo.fieldOptionAnnoInfoList(classLoader)) {
hasOptions = true;
if (null == firstArg) {
firstArg = fieldOptionAnnoInfo.fieldName();
} else {
hasMoreArgs = true;
}
}
S.Buffer sb = S.buffer(commandName);
if (hasOptions) {
sb.append(" [options]");
}
if (null != firstArg) {
sb.append(" ");
sb.append("[").append(firstArg);
if (hasMoreArgs) {
sb.append("...");
}
sb.append("]");
}
return $.T2(sb.toString(), helpMsg());
}
public CommandMethodMetaInfo mode(Act.Mode mode) {
this.mode = mode;
return this;
}
public Act.Mode mode() {
return mode;
}
public CommandMethodMetaInfo invokeStaticMethod() {
invokeType = InvokeType.STATIC;
return this;
}
public CommandMethodMetaInfo invokeInstanceMethod() {
invokeType = InvokeType.VIRTUAL;
return this;
}
public boolean isStatic() {
return InvokeType.STATIC == invokeType;
}
public CommandMethodMetaInfo propertySpec(PropertySpec.MetaInfo propertySpec) {
this.propertySpec = propertySpec;
return this;
}
public PropertySpec.MetaInfo propertySpec() {
return propertySpec;
}
public CommandMethodMetaInfo returnType(Type type) {
returnType = ReturnTypeInfo.of(type);
return this;
}
public Type returnType() {
return returnType.type();
}
public CommandMethodMetaInfo addParam(CommandParamMetaInfo paramInfo) {
params.add(paramInfo);
return this;
}
public C.List<CommandParamMetaInfo> params() {
return C.list(params);
}
public CommandParamMetaInfo param(int id) {
return params.get(id);
}
public int paramCount() {
return params.size();
}
public CommandMethodMetaInfo addLead(String lead) {
if (null == lead) {
return this;
}
if (optionLeads.contains(lead)) {
throw E.unexpected("Duplicate option lead %s found on %s.%s", lead, clsInfo.className(), methodName);
}
optionLeads.add(lead);
return this;
}
@Override
public int hashCode() {
return $.hc(methodName, invokeType, clsInfo, params);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof CommandMethodMetaInfo) {
CommandMethodMetaInfo that = $.cast(obj);
return $.eq(that.methodName, this.methodName)
&& $.eq(that.invokeType, this.invokeType)
&& $.eq(that.params, this.params)
&& $.eq(that.clsInfo, this.clsInfo);
}
return false;
}
@Override
protected void releaseResources() {
super.releaseResources();
clsInfo.destroy();
Destroyable.Util.destroyAll(params, ApplicationScoped.class);
}
}