package ca.svarb.utils;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import ca.svarb.jyacl.Attribute;
import ca.svarb.jyacl.CliOption;
import ca.svarb.jyacl.annotations.Help;
import ca.svarb.jyacl.annotations.Mandatory;
import ca.svarb.jyacl.annotations.Unique;
/**
* This class analyzes an interface and determines
* what "Command Line Options" the interface defines
* through the interface method names, annotations, and
* the methods return types.
*
* CliOption.getName = name of attribute defined by the method name
* CliOption.getReturnType = method return type.
* Supported types are String, Integer, Date, Boolean, an enum or another interface
* CliOption.isMandatory = true if method has "@mandatory" annotation
* CliOption.getHelpText = value of "@help" annotation, or "" if no annotation
*/
public class ClassReader {
private Class<? extends Object> t;
private List<CliOption> cliOptions;
private List<CliOption> allCliOptions;
/**
* Create a ClassReader for the given type.
* @param t
* @throws IllegalArgumentException If a method in t returns an unsupported type.
*/
public ClassReader(Class<? extends Object> t) {
this.t=t;
this.cliOptions=initCliOptions();
this.allCliOptions=initAllOptions();
}
/**
* Converts the class methods to CliOptions and
* returns the list.
* @return
*/
public List<CliOption> getCliOptions() {
return cliOptions;
}
/**
* Converts the class methods to CliOptions then
* also converts methods from any interfaces returned
* to CliOptions and returns the entire list.
* @return
*/
public List<CliOption> getAllCliOptions() {
return allCliOptions;
}
private List<CliOption> initCliOptions() {
List<CliOption> options = new ArrayList<CliOption>();
Method[] methods = t.getMethods();
for (Method method : methods) {
Class<?> methodReturnType = method.getReturnType();
if ( methodReturnType!=String.class &&
methodReturnType!=Integer.class &&
methodReturnType!=Boolean.class &&
!methodReturnType.isEnum() &&
!methodReturnType.isInterface() ) {
throw new IllegalArgumentException("Unsupported return type for method \""+method+"\"");
}
boolean isMandatory=method.isAnnotationPresent(Mandatory.class);
boolean isUnique=method.isAnnotationPresent(Unique.class);
if ( isMandatory && isUnique ) {
throw new IllegalArgumentException("Method \""+method+"\" marked with both @Mandatory and @Unique - annotations cannot be used together.");
}
String[] helpText=null;
if ( method.isAnnotationPresent(Help.class)) {
helpText = method.getAnnotation(Help.class).value();
}
Attribute attribute = new Attribute(method.getName());
if ( isUnique ) {
options.add(new CliOption(attribute.getName(), methodReturnType, helpText));
} else {
options.add(new CliOption(attribute.getName(), methodReturnType, isMandatory, helpText));
}
}
return options;
}
private List<CliOption> initAllOptions() {
List<CliOption> options = initCliOptions();
// Append interface methods
Method[] methods = t.getMethods();
for (Method method : methods) {
Class<?> methodReturnType = method.getReturnType();
if ( methodReturnType.isInterface() ) {
ClassReader interfaceReader = new ClassReader(methodReturnType);
List<CliOption> interfaceOptions = interfaceReader.getCliOptions();
for (CliOption cliOption : interfaceOptions) {
if(!options.contains(cliOption)) {
options.add(cliOption);
}
}
}
}
return options;
}
}