//______________________________________________________________________________ // // This code is stolen from org.dcache.services.AbstractCell // // $Id$ // $Author$ //______________________________________________________________________________ package gov.fnal.srm.util; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.dcache.util.Args; public class OptionParser { public static Set<String> getOptions(Object o){ Set<String> set = new HashSet<>(); Class<?> c = o.getClass(); while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option != null) { set.add(option.name()); } } c = c.getSuperclass(); } return set; } /** * Returns string value of option argument * or default. */ public static String getOption(Option option, Args args) throws IllegalArgumentException { return getOption(option,args,true); } /** * Returns string value of option argument * or default or null */ public static String getOption(Option option, Args args, boolean setDefault) throws IllegalArgumentException { String s; s = args.getOption(option.name()); if (s != null ) { if (s.length()==0 && !option.required()) { return "true"; // to support switch type options } else { return s; } } if (option.required()) { throw new IllegalArgumentException(option.name() + " is a required argument"); } if (setDefault) { return option.defaultValue(); } return s; } /** * Checks if list of options contains option names that do not * match class field names. */ public static void checkOptions(Object o, Args args) { for (String optionName : args.optionsAsMap().keySet()) { Boolean exists=false; Class<?> c = o.getClass(); while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option==null) { continue; } if (option.name().equals(optionName)) { exists = true; break; } } c = c.getSuperclass(); } if (!exists) { throw new IllegalArgumentException("Unknown option specified : -"+optionName); } } } /** * Checks if there are options set to nulls */ public static void checkNullOptions(Object o, String ... names) throws IllegalArgumentException { boolean haveNullOptions=false; StringBuilder sb = new StringBuilder(); Class<?> c = o.getClass(); while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option != null) { for (String s: names) { if (option.name().equals(s)) { try { field.setAccessible(true); Object value = field.get(o); if (value==null) { sb.append(" ").append(option.name()) .append(" option must be set \n"); haveNullOptions=true; } break; } catch (IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option "+ option.name(), e); } } } } } c = c.getSuperclass(); } if (haveNullOptions) { throw new IllegalArgumentException(sb.toString()); } } /** * Checks if there are options set to nulls */ public static String printOptions(Object o, String ... names) throws IllegalArgumentException { StringBuilder sb = new StringBuilder(); Class<?> c = o.getClass(); int maxlength=0; int nblanks=3; for (String s:names) { if(s.length()>maxlength) { maxlength = s.length(); } } int indent=maxlength+nblanks+2; int width=80-indent; while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option != null) { for (String s: names) { if (option.name().equals(s)) { try { field.setAccessible(true); Object value = field.get(o); for(int i=0;i<nblanks;i++) { sb.append(' '); } sb.append("-").append(option.name()); if (field.getType()!= Boolean.TYPE) { sb.append('='); } else { sb.append(' '); } for (int i=option.name().length();i<maxlength;i++) { sb.append(' '); } if (field.getType()!= Boolean.TYPE) { String lines=splitStringIntoSentences(option.description(), indent, width); sb.append(lines).append('\n'); for (int i=0;i<indent;i++) { sb.append(' '); } sb.append("current value is ") .append(value != null ? value : "null(not set) ") .append(" ").append(option.unit()) .append("\n"); } else { String lines=splitStringIntoSentences(option.description()+"(switch)", indent, width); sb.append(lines).append('\n'); for (int i=0;i<indent;i++) { sb.append(' '); } sb.append("current value is ") .append(value).append("\n"); } } catch (IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option "+ option.name(), e); } } } } } c = c.getSuperclass(); } return sb.toString(); } /** * Sets class fields to their default values */ public static <T> void setDefaults(T t) { Class<?> c = t.getClass(); while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option==null) { continue; } try { field.setAccessible(true); String s = option.defaultValue(); Object value; if (s != null && s.length() > 0) { try { value=toType(s,field.getType()); field.set(t,value); } catch (ClassCastException e) { throw new IllegalArgumentException("Cannot convert '"+ s + "' to " + field.getType(), e); } } } catch (SecurityException | IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option "+ option.name(), e); } } c = c.getSuperclass(); } } /** * Sets class fields to the option values specified in args * or default values. * @return Copy of supplied Args without any option that was used. */ public static <T> Args parseOptions(T t, Args args) { List<String> consumedOptions = new ArrayList<>(); Class<?> c = t.getClass(); while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option==null) { continue; } try { field.setAccessible(true); String s = getOption(option,args); Object value; if (s != null && s.length() > 0) { try { value=toType(s,field.getType()); field.set(t,value); } catch (ClassCastException e) { throw new IllegalArgumentException("Cannot convert '"+ s + "' to " + field.getType(), e); } } if (args.getOption(option.name()) != null) { consumedOptions.add(option.name()); } } catch (SecurityException | IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option "+ option.name(), e); } } c = c.getSuperclass(); } return args.removeOptions(consumedOptions.toArray(new String[consumedOptions.size()])); } /** * Sets class fields to the option values specified in args. * */ public static <T> void parseSpecifiedOptions(T t, Args args) { checkOptions(t,args); Class<?> c = t.getClass(); while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option==null) { continue; } try { field.setAccessible(true); String s = getOption(option,args,false); Object value; if (s != null && s.length() > 0) { try { value=toType(s,field.getType()); field.set(t,value); } catch (ClassCastException e) { throw new IllegalArgumentException("Cannot convert '"+ s + "' to " + field.getType(), e); } } } catch (SecurityException | IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option "+ option.name(), e); } } c = c.getSuperclass(); } } public static <T> void parseOption(T t, String optionName, Args args) { Class<?> c = t.getClass(); boolean exists = false; while(c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option==null) { continue; } if (!option.name().equals(optionName)) { continue; } exists=true; try { field.setAccessible(true); String s = getOption(option,args); Object value; if (s != null && s.length() > 0) { try { value=toType(s,field.getType()); field.set(t,value); } catch (ClassCastException e) { throw new IllegalArgumentException("Cannot convert '"+ s + "' to " + field.getType(), e); } } } catch (SecurityException | IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option "+ option.name(), e); } } c = c.getSuperclass(); } if (!exists) { throw new IllegalArgumentException("Unknown option specified : -"+optionName); } } /** * Writes information about all options (Option annotated fields) * to a writer. */ protected <T> void writeOptions(T t, PrintWriter out) { Class<?> c = t.getClass(); while (c!=null) { for (Field field : c.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option==null) { continue; } try { if (option.log()) { field.setAccessible(true); Object value = field.get(t); String description = option.description(); String unit = option.unit(); if (description.length() == 0) { description = option.name(); } out.println(description + " is " + value + " " + unit); } } catch (SecurityException | IllegalAccessException e) { throw new RuntimeException("Bug detected while processing option " + option.name(), e); } } c = c.getSuperclass(); } } @SuppressWarnings("unchecked") public static <T> T toType(final Object object, final Class<T> type) { T result = null; if (object == null || "null".equalsIgnoreCase(object.toString()) ) { // Initialize primitive types: if (type == Boolean.TYPE) { result = ((Class<T>) Boolean.class).cast(false); } else if (type == Byte.TYPE) { result = ((Class<T>) Byte.class).cast(0); } else if (type == Character.TYPE) { result = ((Class<T>) Character.class).cast(0); } else if (type == Double.TYPE) { result = ((Class<T>) Double.class).cast(0.0); } else if (type == Float.TYPE) { result = ((Class<T>) Float.class).cast(0.0); } else if (type == Integer.TYPE) { result = ((Class<T>) Integer.class).cast(0); } else if (type == Long.TYPE) { result = ((Class<T>) Long.class).cast(0); } else if (type == Short.TYPE) { result = ((Class<T>) Short.class).cast(0); } } else { final String so = object.toString(); //custom type conversions: if (type == BigInteger.class) { result = type.cast(new BigInteger(so)); } else if (type == Boolean.class || type == Boolean.TYPE) { Boolean r; if ("1".equals(so) || "true".equalsIgnoreCase(so) || "yes".equalsIgnoreCase(so) || "on".equalsIgnoreCase(so) || "enabled".equalsIgnoreCase(so)) { r = Boolean.TRUE; } else if ("0".equals(object) || "false".equalsIgnoreCase(so) || "no".equalsIgnoreCase(so) || "off".equalsIgnoreCase(so) || "disabled".equalsIgnoreCase(so)) { r = Boolean.FALSE; } else { r = Boolean.valueOf(so); } if (type == Boolean.TYPE) { result = ((Class<T>) Boolean.class).cast(r); //avoid ClassCastException through autoboxing } else { result = type.cast(r); } } else if (type == Byte.class || type == Byte.TYPE) { Byte i=Byte.valueOf(so); if (type == Byte.TYPE) { result = ((Class<T>) Byte.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else if (type == Character.class || type == Character.TYPE) { Character i = so.charAt(0); if (type == Character.TYPE) { result = ((Class<T>) Character.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else if (type == Double.class || type == Double.TYPE) { Double i = Double.valueOf(so); if (type == Double.TYPE) { result = ((Class<T>) Double.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else if (type == Float.class || type == Float.TYPE) { Float i = Float.valueOf(so); if (type == Float.TYPE) { result = ((Class<T>) Float.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else if (type == Integer.class || type == Integer.TYPE) { Integer i = Integer.valueOf(so); if (type == Integer.TYPE) { result = ((Class<T>) Integer.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else if (type == Long.class || type == Long.TYPE) { Long i = Long.valueOf(so); if (type == Long.TYPE) { result = ((Class<T>) Long.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else if (type == Short.class || type == Short.TYPE) { Short i = Short.valueOf(so); if (type == Short.TYPE) { result = ((Class<T>) Short.class).cast(i); //avoid ClassCastException through autoboxing } else { result = type.cast(i); } } else { try { Constructor<T> constructor = type.getConstructor(String.class); result = constructor.newInstance(object); } catch (NoSuchMethodException e) { //hard cast: result = type.cast(object); } catch (SecurityException e) { //hard cast: result = type.cast(object); } catch (InstantiationException e) { //hard cast: result = type.cast(object); } catch (IllegalAccessException e) { //hard cast: result = type.cast(object); } catch (InvocationTargetException e) { //hard cast: result = type.cast(object); } } } return result; } public static String splitStringIntoSentences(String text, int start, int width){ if (text.length()<=width) { return text; } StringBuilder sb = new StringBuilder(); String[] words=text.split(" "); int currentLineWidth=0; for (String word: words) { currentLineWidth+=word.length()+1; if (currentLineWidth>width-1) { sb.append('\n'); for (int j=0;j<start;j++) { sb.append(' '); } currentLineWidth=0; } sb.append(word).append(' '); } return sb.toString(); } }