All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.stanford.nlp.util.Execution Maven / Gradle / Ivy

package edu.stanford.nlp.util;

import edu.stanford.nlp.util.logging.StanfordRedwoodConfiguration;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.annotation.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import static edu.stanford.nlp.util.logging.Redwood.Util.*;

/**
 * A class to set command line options. To use, create a static class into which you'd like
 * to put your properties. Then, for each field, set the annotation:
 *
 * 
 *   
 *     import edu.stanford.nlp.util.Execution.Option
 *
 *     class Props {
 *       @Option(name="anIntOption", required=false, gloss="This is an int")
 *       public static int anIntOption = 7; // default value is 7
 *       @Option(name="anotherOption", required=false)
 *       public static File aCastableOption = new File("/foo");
 *     }
 *   
 * 
* *

* You can then set options with {@link Execution#exec(Runnable, String[])}, * or with {@link Execution#fillOptions(java.util.Properties)}. *

* *

* If your default classpath has many classes in it, you can select a subset of them * by using {@link Execution#fillOptions(Class[], java.util.Properties)}, or some variant. *

* *

* A complete toy example looks like this: *

* *
 *   
 *     import java.util.Properties;
 *
 *     import edu.stanford.nlp.util.Execution;
 *     import edu.stanford.nlp.util.StringUtils;
 *
 *     public class Foo {
 *
 *       @Execution.Option(name="bar", gloss="This is a string option.", required=true)
 *       private static String BAR = null;
 *
 *       public static void main(String[] args) {
 *         // Parse the arguments
 *         Properties props = StringUtils.argsToProperties(args);
 *         Execution.fillOptions(new Class[]{ Foo.class, Execution.class }, props);
 *
 *         System.err.println(INPUT);
 *       }
 *     }
 *   
 * 
* */ public class Execution { @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Option { String name() default ""; String gloss() default ""; boolean required() default false; String alt() default ""; } @SuppressWarnings("MismatchedReadAndWriteOfArray") private static final String[] IGNORED_JARS = { }; private static final Class[] BOOTSTRAP_CLASSES = { Execution.class, }; @Option(name = "option_classes", gloss = "Fill options from these classes") public static Class[] optionClasses = null; @Option(name = "threads", gloss = "Number of threads on machine") public static int threads = Runtime.getRuntime().availableProcessors(); @Option(name = "host", gloss = "Name of computer we are running on") public static String host = "(unknown)"; @SuppressWarnings("FieldCanBeLocal") @Option(name = "strict", gloss = "If true, make sure that all options passed in are used somewhere") private static boolean strict = false; @SuppressWarnings("FieldCanBeLocal") @Option(name = "exec.verbose", gloss = "If true, print options as they are set.") private static boolean verbose = false; static { try { host = InetAddress.getLocalHost().getHostName(); } catch (Exception ignored) { } } /** * A lazy iterator over files, not loading all of them into memory at once. */ public static class LazyFileIterator implements Iterator { private FilenameFilter filter; private File[] dir; private Stack parents = new Stack<>(); private Stack indices = new Stack<>(); private int toReturn = -1; public LazyFileIterator(File path, final String filter) { this(path, (file, name) -> { String filePath = (file.getPath() + "/" + name); return new File(filePath).isDirectory() || filePath.matches(filter); }); } public LazyFileIterator(File dir, FilenameFilter filter) { if (!dir.exists()) throw new IllegalArgumentException("Could not find directory: " + dir.getPath()); if (!dir.isDirectory()) throw new IllegalArgumentException("Not a directory: " + dir.getPath()); this.filter = filter; this.dir = dir.listFiles(filter); enqueue(); } private void enqueue() { toReturn += 1; boolean good = (toReturn < dir.length && !dir[toReturn].isDirectory()); while (!good) { if (toReturn >= dir.length) { //(case: pop) if (parents.isEmpty()) { toReturn = -1; return; //this is where we exit } else { dir = parents.pop(); toReturn = indices.pop(); } } else if (dir[toReturn].isDirectory()) { //(case: push) parents.push(dir); indices.push(toReturn + 1); dir = dir[toReturn].listFiles(filter); toReturn = 0; } else { throw new IllegalStateException("File is invalid, but in range and not a directory: " + dir[toReturn]); } //(check if good) good = (toReturn < dir.length && !dir[toReturn].isDirectory()); } // if we reach here we found something } @Override public boolean hasNext() { return toReturn >= 0; } @Override public File next() { if (toReturn >= dir.length || toReturn < 0) throw new IllegalStateException("No more elements!"); File rtn = dir[toReturn]; enqueue(); return rtn; } @Override public void remove() { throw new IllegalArgumentException("NOT IMPLEMENTED"); } } /* * ---------- * OPTIONS * ---------- */ private static void fillField(Object instance, Field f, String value) { //--Verbose if (verbose) { Option opt = f.getAnnotation(Option.class); StringBuilder b = new StringBuilder("setting ").append(f.getDeclaringClass().getName()).append("#").append(f.getName()).append(" "); if (opt != null) { b.append("[").append(opt.name()).append("] "); } b.append("to: ").append(value); log(b.toString()); } try { //--Permissions boolean accessState = true; if (Modifier.isFinal(f.getModifiers())) { runtimeException("Option cannot be final: " + f); } if (!f.isAccessible()) { accessState = false; f.setAccessible(true); } //--Set Value Object objVal = MetaClass.cast(value, f.getGenericType()); if (objVal != null) { if (objVal.getClass().isArray()) { //(case: array) Object[] array = (Object[]) objVal; // error check if (!f.getType().isArray()) { runtimeException("Setting an array to a non-array field. field: " + f + " value: " + Arrays.toString(array) + " src: " + value); } // create specific array Object toSet = Array.newInstance(f.getType().getComponentType(), array.length); for (int i = 0; i < array.length; i++) { Array.set(toSet, i, array[i]); } // set value f.set(instance, toSet); } else { //case: not array f.set(instance, objVal); } } else { runtimeException("Cannot assign option field: " + f + " value: " + value + "; invalid type"); } //--Permissions if (!accessState) { f.setAccessible(false); } } catch (IllegalArgumentException e) { err(e); runtimeException("Cannot assign option field: " + f.getDeclaringClass().getCanonicalName() + "." + f.getName() + " value: " + value + " cause: " + e.getMessage()); } catch (IllegalAccessException e) { err(e); runtimeException("Cannot access option field: " + f.getDeclaringClass().getCanonicalName() + "." + f.getName()); } catch (Exception e) { err(e); runtimeException("Cannot assign option field: " + f.getDeclaringClass().getCanonicalName() + "." + f.getName() + " value: " + value + " cause: " + e.getMessage()); } } @SuppressWarnings("rawtypes") private static Class filePathToClass(String cpEntry, String path) { if (path.length() <= cpEntry.length()) { throw new IllegalArgumentException("Illegal path: cp=" + cpEntry + " path=" + path); } if (path.charAt(cpEntry.length()) != '/') { throw new IllegalArgumentException("Illegal path: cp=" + cpEntry + " path=" + path); } path = path.substring(cpEntry.length() + 1); path = path.replaceAll("/", ".").substring(0, path.length() - 6); try { return Class.forName(path, false, ClassLoader.getSystemClassLoader()); } catch (ClassNotFoundException e) { throw fail("Could not load class at path: " + path); } catch (NoClassDefFoundError ex) { warn("Class at path " + path + " is unloadable"); return null; } } private static boolean isIgnored(String path) { for (String ignore : IGNORED_JARS) { if (path.endsWith(ignore)) { return true; } } return false; } public static Class[] getVisibleClasses() { //--Variables List> classes = new ArrayList<>(); // (get classpath) String pathSep = System.getProperty("path.separator"); String[] cp = System.getProperties().getProperty("java.class.path", null).split(pathSep); // --Fill Options // (get classes) for (String entry : cp) { log("Checking cp " + entry); //(should skip?) if (entry.equals(".") || entry.trim().length() == 0) { continue; } //(no, don't skip) File f = new File(entry); if (f.isDirectory()) { // --Case: Files LazyFileIterator iter = new LazyFileIterator(f, ".*class$"); while (iter.hasNext()) { //(get the associated class) Class clazz = filePathToClass(entry, iter.next().getPath()); if (clazz != null) { //(add the class if it's valid) classes.add(clazz); } } } else //noinspection StatementWithEmptyBody if (!isIgnored(entry)) { // --Case: Jar try { JarFile jar = new JarFile(f); Enumeration e = jar.entries(); while (e.hasMoreElements()) { //(for each jar file element) JarEntry jarEntry = e.nextElement(); String clazz = jarEntry.getName(); if (clazz.matches(".*class$")) { //(if it's a class) clazz = clazz.substring(0, clazz.length() - 6) .replaceAll("/", "."); //(add it) try { classes.add( Class.forName(clazz, false, ClassLoader.getSystemClassLoader())); } catch (ClassNotFoundException ex) { warn("Could not load class in jar: " + f + " at path: " + clazz); } catch (NoClassDefFoundError ex) { debug("Could not scan class: " + clazz + " (in jar: " + f + ")"); } } } } catch (IOException e) { warn("Could not open jar file: " + f + "(are you sure the file exists?)"); } } else { //case: ignored jar } } return classes.toArray(new Class[classes.size()]); } /** * Get all the declared fields of this class and all super classes. */ private static Field[] scrapeFields(Class clazz) throws Exception { List fields = new ArrayList<>(); while (clazz != null && !clazz.equals(Object.class)) { fields.addAll(Arrays.asList(clazz.getDeclaredFields())); clazz = clazz.getSuperclass(); } return fields.toArray(new Field[fields.size()]); } @SuppressWarnings("rawtypes") protected static Map fillOptionsImpl( Object[] instances, Class[] classes, Properties options, boolean ensureAllOptions) { //--Create Class->Object Mapping Map class2object = new HashMap<>(); if (instances != null) { for (int i = 0; i < classes.length; ++i) { assert instances[i].getClass() == classes[i]; class2object.put(classes[i], instances[i]); Class mySuper = instances[i].getClass().getSuperclass(); while (mySuper != null && !mySuper.equals(Object.class)) { if (!class2object.containsKey(mySuper)) { class2object.put(mySuper, instances[i]); } mySuper = mySuper.getSuperclass(); } } } //--Get Fillable Options Map canFill = new HashMap<>(); Map> required = new HashMap<>(); /* */ Map interner = new HashMap<>(); for (Class c : classes) { Field[] fields; try { fields = scrapeFields(c); } catch (Throwable e) { debug("Could not check fields for class: " + c.getName() + " (caused by " + e.getClass() + ": " + e.getMessage() + ")"); continue; } boolean someOptionFilled = false; boolean someOptionFound = false; for (Field f : fields) { Option o = f.getAnnotation(Option.class); if (o != null) { someOptionFound = true; //(check if field is static) if ((f.getModifiers() & Modifier.STATIC) == 0 && instances == null) { continue; } someOptionFilled = true; //(required marker) Pair mark = Pair.makePair(false, false); if (o.required()) { mark = Pair.makePair(true, false); } //(add main name) String name = o.name().toLowerCase(); if (name.equals("")) { name = f.getName().toLowerCase(); } if (canFill.containsKey(name)) { String name1 = canFill.get(name).getDeclaringClass().getCanonicalName() + "." + canFill.get(name).getName(); String name2 = f.getDeclaringClass().getCanonicalName() + "." + f.getName(); if (!name1.equals(name2)) { runtimeException("Multiple declarations of option " + name + ": " + name1 + " and " + name2); } else { err("Class is in classpath multiple times: " + canFill.get(name).getDeclaringClass().getCanonicalName()); } } canFill.put(name, f); required.put(name, mark); interner.put(name, name); //(add alternate names) if (!o.alt().equals("")) { for (String alt : o.alt().split(" *, *")) { alt = alt.toLowerCase(); if (canFill.containsKey(alt) && !alt.equals(name)) throw new IllegalArgumentException("Multiple declarations of option " + alt + ": " + canFill.get(alt) + " and " + f); canFill.put(alt, f); if (mark.first) required.put(alt, mark); interner.put(alt, name); } } } } //(check to ensure that something got filled, if any @Option annotation was found) if (someOptionFound && !someOptionFilled) { warn("found @Option annotations in class " + c + ", but didn't set any of them (all options were instance variables and no instance given?)"); } } //--Fill Options for (Object rawKey : options.keySet()) { String rawKeyStr = rawKey.toString(); String key = rawKey.toString().toLowerCase(); // (get values) String value = options.get(rawKey).toString(); assert value != null; Field target = canFill.get(key); // (mark required option as fulfilled) Pair mark = required.get(key); if (mark != null && mark.first) { required.put(key, Pair.makePair(true, true)); } // (fill the field) if (target != null) { // (case: declared option) fillField(class2object.get(target.getDeclaringClass()), target, value); } else if (ensureAllOptions) { // (case: undeclared option) // split the key int lastDotIndex = rawKeyStr.lastIndexOf('.'); if (lastDotIndex < 0) { err("Unrecognized option: " + key); continue; } if (!rawKeyStr.startsWith("log.")) { // ignore Redwood options String className = rawKeyStr.substring(0, lastDotIndex); String fieldName = rawKeyStr.substring(lastDotIndex + 1); // get the class Class clazz = null; try { clazz = ClassLoader.getSystemClassLoader().loadClass(className); } catch (Exception e) { err("Could not set option: " + rawKey + "; either the option is mistyped, not defined, or the class " + className + " does not exist."); } // get the field if (clazz != null) { try { target = clazz.getField(fieldName); } catch (Exception e) { err("Could not set option: " + rawKey + "; no such field: " + fieldName + " in class: " + className); } if (target != null) { log("option overrides " + target + " to '" + value + "'"); fillField(class2object.get(target.getDeclaringClass()), target, value); } else { err("Could not set option: " + rawKey + "; no such field: " + fieldName + " in class: " + className); } } } } } //--Ensure Required boolean good = true; for (String key : required.keySet()) { Pair mark = required.get(key); if (mark.first && !mark.second) { err("Missing required option: " + interner.get(key) + " "); required.put(key, Pair.makePair(true, true)); //don't duplicate error messages good = false; } } if (!good) { throw new RuntimeException("not able to parse properties!!!!"); //System.exit(1); } return canFill; } protected static Map fillOptionsImpl( Object[] instances, Class[] classes, Properties options) { return fillOptionsImpl(instances, classes, options, strict); } /* * ---------- * EXECUTION * ---------- */ public static void fillOptions(Class[] classes, Properties options) { fillOptionsImpl(null, classes, options); } public static void fillOptions(Class clazz, Properties options) { fillOptionsImpl(null, new Class[]{ clazz }, options); } public static void fillOptions(Properties props, String[] args) { //(convert to map) Properties options = StringUtils.argsToProperties(args); for (String key : props.stringPropertyNames()) { options.setProperty(key, props.getProperty(key)); } //(bootstrap) Map bootstrapMap = fillOptionsImpl(null, BOOTSTRAP_CLASSES, options, false); //bootstrap for (String key : bootstrapMap.keySet()) { options.remove(key); } //(fill options) Class[] visibleClasses = optionClasses; if (visibleClasses == null) visibleClasses = getVisibleClasses(); //get classes fillOptionsImpl(null, visibleClasses, options);//fill } @SuppressWarnings("UnusedDeclaration") public static void fillOptions(Class[] optionClasses, Properties props, String[] args) { Execution.optionClasses = optionClasses; fillOptions(props, args); } public static void fillOptions(Properties props) { fillOptions(props, new String[0]); } public static void fillOptions(Class[] classes, String[] args) { Properties options = StringUtils.argsToProperties(args); //get options fillOptionsImpl(null, BOOTSTRAP_CLASSES, options, false); //bootstrap fillOptionsImpl(null, classes, options); } public static void fillOptions(Class clazz, String[] args) { Class[] classes = new Class[1]; classes[0] = clazz; fillOptions(classes, args); } public static void fillOptions(Object[] instances, Properties options) { Class[] classes = new Class[instances.length]; for (int i = 0; i < classes.length; ++i) { classes[i] = instances[i].getClass(); } fillOptionsImpl(instances, classes, options); } public static void fillOptions(Object instance, Properties options) { fillOptions(new Object[]{ instance }, options); } public static void fillOptions(Object[] instances, String[] args) { Properties options = StringUtils.argsToProperties(args); //get options fillOptionsImpl(null, BOOTSTRAP_CLASSES, options, false); //bootstrap Class[] classes = new Class[instances.length]; for (int i = 0; i < classes.length; ++i) { classes[i] = instances[i].getClass(); } fillOptionsImpl(instances, classes, options); } public static void fillOptions(Object instance, String[] args) { fillOptions(new Object[]{ instance }, args); } public static void exec(Runnable toRun) { exec(toRun, new String[0]); } public static void exec(Runnable toRun, Class[] optionClasses) { Execution.optionClasses = optionClasses; exec(toRun, new String[0]); } public static void exec(Runnable toRun, String[] args) { exec(toRun, args, false); } public static void exec(Runnable toRun, String[] args, Class... optionClasses) { Execution.optionClasses = optionClasses; exec(toRun, args, false); } public static void exec(Runnable toRun, String[] args, Class[] optionClasses, boolean exit) { Execution.optionClasses = optionClasses; exec(toRun, StringUtils.argsToProperties(args), exit); } public static void exec(Runnable toRun, String[] args, boolean exit) { exec(toRun, StringUtils.argsToProperties(args), exit); } public static void exec(Runnable toRun, Properties options) { exec(toRun, options, false); } public static void exec(Runnable toRun, Properties options, boolean exit) { //--Init //(bootstrap) Map bootstrapMap = fillOptionsImpl(null, BOOTSTRAP_CLASSES, options, false); //bootstrap for (String key : bootstrapMap.keySet()) { options.remove(key); } startTrack("init"); //(fill options) Class[] visibleClasses = optionClasses; if (visibleClasses == null) visibleClasses = getVisibleClasses(); //get classes fillOptionsImpl(null, visibleClasses, options);//fill endTrack("init"); // -- Setup Logging StanfordRedwoodConfiguration.apply(options); //--Run Program int exitCode = 0; startTrack("main"); try { toRun.run(); } catch (Throwable t) { log(FORCE, t); exitCode = 1; } endTracksTo("main"); // end main if (exit) { throw new RuntimeException("not able to parse properties!!!"); //System.exit(exitCode); } } private static String threadRootClass() { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); StackTraceElement elem = trace[trace.length - 1]; return elem.getClassName(); } @SuppressWarnings("UnusedDeclaration") public static void usageAndExit(String[] expectedArgs) { String clazz = threadRootClass(); StringBuilder b = new StringBuilder(); b.append("USAGE: ").append(clazz).append(" "); for (String arg : expectedArgs) { b.append(arg).append(" "); } System.out.println(b.toString()); System.exit(0); } @SuppressWarnings("UnusedDeclaration") public static void usageAndExit(Map argToFlagsMap) { String clazz = threadRootClass(); StringBuilder b = new StringBuilder(); b.append("USAGE: ").append(clazz).append("\n\t"); for (String arg : argToFlagsMap.keySet()) { String[] flags = argToFlagsMap.get(arg); if (flags == null || flags.length == 0) { throw new IllegalArgumentException( "No flags registered for arg: " + arg); } b.append("{"); for (int i = 0; i < flags.length - 1; i++) { b.append(flags[i]).append(","); } b.append(flags[flags.length - 1]).append("}"); } System.out.println(b.toString()); System.exit(0); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy