From: aclement Date: Fri, 5 Aug 2011 16:21:41 +0000 (+0000) Subject: refactoring: pulled testcode up into this class from subtypes X-Git-Tag: V1_6_12M2~40 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=be14485b795b8a31b6a7f4680bb7a28321be4b18;p=aspectj.git refactoring: pulled testcode up into this class from subtypes --- diff --git a/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java b/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java index 9ebf1935a..d4e46973d 100644 --- a/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java +++ b/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java @@ -15,7 +15,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Stack; @@ -24,6 +28,11 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.apache.commons.digester.Digester; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.LocalVariable; +import org.aspectj.apache.bcel.classfile.LocalVariableTable; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; import org.aspectj.tools.ajc.AjcTestCase; import org.aspectj.tools.ajc.CompilationResult; import org.aspectj.util.FileUtil; @@ -298,6 +307,63 @@ public abstract class XMLBasedAjcTestCase extends AjcTestCase { assertTrue("File " + file + " should have been updated", f.lastModified() > sinceTime); } + public SyntheticRepository createRepos(File cpentry) { + ClassPath cp = new ClassPath(cpentry + File.pathSeparator + System.getProperty("java.class.path")); + return SyntheticRepository.getInstance(cp); + } + + public JavaClass getClassFrom(File where, String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos(where); + return repos.loadClass(clazzname); + } + + /** + * Sort it by name then start position + */ + public List sortedLocalVariables(LocalVariableTable lvt) { + List l = new ArrayList(); + LocalVariable lv[] = lvt.getLocalVariableTable(); + for (int i = 0; i < lv.length; i++) { + LocalVariable lvEntry = lv[i]; + l.add(lvEntry); + } + Collections.sort(l, new MyComparator()); + return l; + } + + public String stringify(LocalVariableTable lvt, int slotIndex) { + LocalVariable lv[] = lvt.getLocalVariableTable(); + LocalVariable lvEntry = lv[slotIndex]; + StringBuffer sb = new StringBuffer(); + sb.append(lvEntry.getSignature()).append(" ").append(lvEntry.getName()).append("(").append(lvEntry.getIndex()) + .append(") start=").append(lvEntry.getStartPC()).append(" len=").append(lvEntry.getLength()); + return sb.toString(); + } + + public String stringify(List l, int slotIndex) { + LocalVariable lvEntry = (LocalVariable) l.get(slotIndex); + StringBuffer sb = new StringBuffer(); + sb.append(lvEntry.getSignature()).append(" ").append(lvEntry.getName()).append("(").append(lvEntry.getIndex()) + .append(") start=").append(lvEntry.getStartPC()).append(" len=").append(lvEntry.getLength()); + return sb.toString(); + } + + public String stringify(LocalVariableTable lvt) { + if (lvt == null) { + return ""; + } + StringBuffer sb = new StringBuffer(); + sb.append("LocalVariableTable. Entries=#" + lvt.getTableLength()).append("\n"); + LocalVariable lv[] = lvt.getLocalVariableTable(); + for (int i = 0; i < lv.length; i++) { + LocalVariable lvEntry = lv[i]; + sb.append(lvEntry.getSignature()).append(" ").append(lvEntry.getName()).append("(").append(lvEntry.getIndex()) + .append(") start=").append(lvEntry.getStartPC()).append(" len=").append(lvEntry.getLength()).append("\n"); + } + + return sb.toString(); + } + public static class CountingFilenameFilter implements FilenameFilter { private String suffix; @@ -318,4 +384,17 @@ public abstract class XMLBasedAjcTestCase extends AjcTestCase { return count; } } + + public static class MyComparator implements Comparator { + public int compare(LocalVariable o1, LocalVariable o2) { + LocalVariable l1 = (LocalVariable) o1; + LocalVariable l2 = (LocalVariable) o2; + if (l1.getName().equals(l2.getName())) { + return l1.getStartPC() - l2.getStartPC(); + } else { + return l1.getName().compareTo(l2.getName()); + } + } + + } } diff --git a/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java b/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java index f8e68ffde..64b136994 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java +++ b/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java @@ -32,960 +32,887 @@ import org.aspectj.bridge.MessageUtil; import org.aspectj.testing.run.IRunIterator; import org.aspectj.testing.util.BridgeUtil; import org.aspectj.testing.util.options.Option; +import org.aspectj.testing.util.options.Option.InvalidInputException; import org.aspectj.testing.util.options.Options; import org.aspectj.testing.util.options.Values; -import org.aspectj.testing.util.options.Option.InvalidInputException; import org.aspectj.testing.xml.IXmlWritable; import org.aspectj.testing.xml.SoftMessage; import org.aspectj.testing.xml.XMLWriter; import org.aspectj.util.LangUtil; /** - * Base class for initialization of components expecting messages, - * options, files/paths, and source locations (resolved files), - * and potentially containing child Spec. + * Base class for initialization of components expecting messages, options, files/paths, and source locations (resolved files), and + * potentially containing child Spec. *

- * initialization: This defines bean/xml setters for all. - * This converts String to IMessage using - * {@link MessageUtil#readMessage(String)} - * and String to ISourceLocation using - * {@link BridgeUtil#makeSourceLocation(input)}. - * See those APIs for input form and limitations. - * A Spec also accepts (or rejects) runtime configuration from a parent - * in {@link adoptParentValues(RT, IMessageHandler)}. - * Since some children Spec may balk but this parent Spec continue, - * use {@link getChildren()} to get the full list of children Spec, - * but {@link getWorkingChildren()} to get the list of children that - * are not being skipped in accordance with runtime configuration. - *

- * subclassing: subclasses wishing other than - * the default behavior for reading String input should override the - * corresponding (bean) setter. They can also override the - * add{foo} methods to get notice or modify objects constructed - * from the input. + * initialization: This defines bean/xml setters for all. This converts String to IMessage using + * {@link MessageUtil#readMessage(String)} and String to ISourceLocation using {@link BridgeUtil#makeSourceLocation(input)}. See + * those APIs for input form and limitations. A Spec also accepts (or rejects) runtime configuration from a parent in {@link + * adoptParentValues(RT, IMessageHandler)}. Since some children Spec may balk but this parent Spec continue, use {@link + * getChildren()} to get the full list of children Spec, but {@link getWorkingChildren()} to get the list of children that are not + * being skipped in accordance with runtime configuration. *

- * bean properties: because this is designed to work - * by standard Java bean introspection, take care to follow - * bean rules when adding methods. In particular, a property - * is illegal if the setter takes a different type than the - * getter returns. That means, e.g., that all List and array[] getters - * should be named "get{property}[List|Array]". Otherwise - * the XML readers will silently fail to set the property - * (perhaps with trace information that the property had - * no write method or was read-only). + * subclassing: subclasses wishing other than the default behavior for reading String input should override the corresponding + * (bean) setter. They can also override the add{foo} methods to get notice or modify objects constructed from the input. *

- * Coordination with writers: because this reads the contents - * of values written by IXmlWritable, they should ensure their - * values are readable. When flattening and unflattening - * lists, the convention is to use the {un}flattenList(..) methods - * in XMLWriter. + * bean properties: because this is designed to work by standard Java bean introspection, take care to follow bean rules when + * adding methods. In particular, a property is illegal if the setter takes a different type than the getter returns. That means, + * e.g., that all List and array[] getters should be named "get{property}[List|Array]". Otherwise the XML readers will silently fail + * to set the property (perhaps with trace information that the property had no write method or was read-only). + *

+ * Coordination with writers: because this reads the contents of values written by IXmlWritable, they should ensure their + * values are readable. When flattening and unflattening lists, the convention is to use the {un}flattenList(..) methods in + * XMLWriter. + * * @see XMLWriter@unflattenList(String) * @see XMLWriter@flattenList(List) */ abstract public class AbstractRunSpec implements IRunSpec { - /** true if we expect to use a staging directory */ - boolean isStaging; - - /** true if this spec permits bad input (e.g., to test error handling) */ - boolean badInput; - - protected String description; - - /** optional source location of the specification itself */ - protected ISourceLocation sourceLocation; - - private BitSet skipSet; - private boolean skipAll; - - protected String xmlElementName; // nonfinal only for clone() - protected final ArrayList /*String*/ keywords; - protected final IMessageHolder /*IMessage*/ messages; - protected final ArrayList /*String*/ options; - protected final ArrayList /*String*/ paths; -// XXXXXunused protected final ArrayList /*ISourceLocation*/ sourceLocations; // XXX remove? - protected final ArrayList /*IRunSpec*/ children; - protected final ArrayList /*DirChanges.Spec*/ dirChanges; - protected XMLNames xmlNames; - protected String comment; - - - /** These options are 1:1 with spec, but set at runtime (not saved) */ - public final RT runtime; - - /** if true, then any child skip causes this to skip */ + /** true if we expect to use a staging directory */ + boolean isStaging; + + /** true if this spec permits bad input (e.g., to test error handling) */ + boolean badInput; + + protected String description; + + /** optional source location of the specification itself */ + protected ISourceLocation sourceLocation; + + private BitSet skipSet; + private boolean skipAll; + + protected String xmlElementName; // nonfinal only for clone() + protected final ArrayList keywords; + protected final IMessageHolder /* IMessage */messages; + protected final ArrayList options; + protected final ArrayList paths; + // XXXXXunused protected final ArrayList /*ISourceLocation*/ sourceLocations; // XXX remove? + protected final ArrayList /* IRunSpec */children; + protected final ArrayList /* DirChanges.Spec */dirChanges; + protected XMLNames xmlNames; + protected String comment; + + /** These options are 1:1 with spec, but set at runtime (not saved) */ + public final RT runtime; + + /** if true, then any child skip causes this to skip */ protected boolean skipIfAnyChildSkipped; // nonfinal only for cloning - public AbstractRunSpec(String xmlElementName) { - this(xmlElementName, true); - } - - public AbstractRunSpec(String xmlElementName, boolean skipIfAnyChildSkipped) { - if (null == xmlElementName) { - xmlElementName = "spec"; - } - this.xmlElementName = xmlElementName; - messages = new MessageHandler(true); - options = new ArrayList(); - paths = new ArrayList(); -// XXXXXunused sourceLocations = new ArrayList(); - keywords = new ArrayList(); - children = new ArrayList(); - dirChanges = new ArrayList(); - xmlNames = XMLNames.DEFAULT; - runtime = new RT(); - this.skipIfAnyChildSkipped = skipIfAnyChildSkipped; - } - - - /** @param comment ignored if null */ - public void setComment(String comment) { - if (!LangUtil.isEmpty(comment)) { - this.comment = comment; - } - } - - public void setStaging(boolean staging) { - isStaging = staging; - } - - public void setBadInput(boolean badInput) { - this.badInput = badInput; - } - - boolean isStaging() { - return isStaging; - } - - // ------- description (title, label...) - public void setDescription(String description) { - this.description = description; - } - - public String getDescription() { - return description; - } - // ------- source location of the spec - - public void setSourceLocation(ISourceLocation sourceLocation) { - this.sourceLocation = sourceLocation; - } - - public ISourceLocation getSourceLocation() { - return sourceLocation; - } - - // ------- keywords - /** @param keyword added after trimming if not empty */ - public void setKeyword(String keyword) { - addKeyword(keyword); - } - - /** @return ((null == s) || (0 == s.trim().length())); */ - public static boolean isEmptyTrimmed(String s) { - return ((null == s) || (0 == s.length()) - || (0 == s.trim().length())); - } - - /** Add keyword if non-empty and not duplicate */ - public void addKeyword(String keyword) { - if (!isEmptyTrimmed(keyword)) { - keyword = keyword.trim(); - if (!keywords.contains(keyword)) { - keywords.add(keyword); - } - } - } - public void setKeywords(String items) { - addKeywords(items); - } - - public void addKeywords(String items) { - if (null != items) { - addKeywords(XMLWriter.unflattenList(items)); - } - } - public void addKeywords(String[] ra) { - if (null != ra) { - for (int i = 0; i < ra.length; i++) { - addKeyword(ra[i]); - } - } - } - public ArrayList getKeywordsList() { - return makeList(keywords); - } - - // ------- options - String args - - /** @return ArrayList of String options */ - public ArrayList getOptionsList() { - return makeList(options); - } - - /** @return String[] of options */ - public String[] getOptionsArray() { - return (String[]) options.toArray(new String[0]); - } - - public void setOption(String option) { - addOption(option); - } - - public void addOption(String option) { - if ((null != option) && (0 < option.length())) { - options.add(option); - } - } - - /** add options (from XML/bean) - removes any existing options */ - public void setOptions(String items) { - this.options.clear(); - addOptions(items); - } - - /** - * Set options, removing any existing options. - * @param options String[] options to use - may be null or empty - */ - public void setOptionsArray(String[] options) { - this.options.clear(); - if (!LangUtil.isEmpty(options)) { - this.options.addAll(Arrays.asList(options)); - } - } - - public void addOptions(String items) { - if (null != items) { - addOptions(XMLWriter.unflattenList(items)); - } - } - - public void addOptions(String[] ra) { - if (null != ra) { - for (int i = 0; i < ra.length; i++) { - addOption(ra[i]); - } - } - } - - // --------------- (String) paths - /** @return ArrayList of String paths */ - public ArrayList getPathsList() { - return makeList(paths); - } - - /** @return String[] of paths */ - public String[] getPathsArray() { - return (String[]) paths.toArray(new String[0]); - } - - public void setPath(String path) { - addPath(path); - } - - public void setPaths(String paths) { - addPaths(paths); - } - - public void addPath(String path) { - if (null != path) { - paths.add(path); - } - } - - public void addPaths(String items) { - if (null != items) { - addPaths(XMLWriter.unflattenList(items)); - } - } - - public void addPaths(String[] ra) { - if (null != ra) { - for (int i = 0; i < ra.length; i++) { - addPath(ra[i]); - } - } - } - - // --------------------- dir changes - public void addDirChanges(DirChanges.Spec dirChangesSpec) { - if (null != dirChangesSpec) { - dirChanges.add(dirChangesSpec); - } - } - - // --------------------- messages - public void setMessage(String message) { - addMessage(message); - } - - public void addMessage(IMessage message) { - if (null != message) { - if (!messages.handleMessage(message)) { - String s = "invalid message: " + message; - throw new IllegalArgumentException(s); - } - } - } - - public void addMessage(String message) { - if (null != message) { - addMessage(BridgeUtil.readMessage(message)); - } - } - - /** this can ONLY work if each item has no internal comma - */ - public void addMessages(String items) { - if (null != items) { - String[] ra = XMLWriter.unflattenList(items); - for (int i = 0; i < ra.length; i++) { - addMessage(ra[i]); - } - } - } - public void addMessages(List messages) { - if (null != messages) { - for (Iterator iter = messages.iterator(); iter.hasNext();) { + public AbstractRunSpec(String xmlElementName) { + this(xmlElementName, true); + } + + public AbstractRunSpec(String xmlElementName, boolean skipIfAnyChildSkipped) { + if (null == xmlElementName) { + xmlElementName = "spec"; + } + this.xmlElementName = xmlElementName; + messages = new MessageHandler(true); + options = new ArrayList(); + paths = new ArrayList(); + // XXXXXunused sourceLocations = new ArrayList(); + keywords = new ArrayList(); + children = new ArrayList(); + dirChanges = new ArrayList(); + xmlNames = XMLNames.DEFAULT; + runtime = new RT(); + this.skipIfAnyChildSkipped = skipIfAnyChildSkipped; + } + + /** @param comment ignored if null */ + public void setComment(String comment) { + if (!LangUtil.isEmpty(comment)) { + this.comment = comment; + } + } + + public void setStaging(boolean staging) { + isStaging = staging; + } + + public void setBadInput(boolean badInput) { + this.badInput = badInput; + } + + boolean isStaging() { + return isStaging; + } + + // ------- description (title, label...) + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + // ------- source location of the spec + + public void setSourceLocation(ISourceLocation sourceLocation) { + this.sourceLocation = sourceLocation; + } + + public ISourceLocation getSourceLocation() { + return sourceLocation; + } + + // ------- keywords + /** @param keyword added after trimming if not empty */ + public void setKeyword(String keyword) { + addKeyword(keyword); + } + + /** @return ((null == s) || (0 == s.trim().length())); */ + public static boolean isEmptyTrimmed(String s) { + return ((null == s) || (0 == s.length()) || (0 == s.trim().length())); + } + + /** Add keyword if non-empty and not duplicate */ + public void addKeyword(String keyword) { + if (!isEmptyTrimmed(keyword)) { + keyword = keyword.trim(); + if (!keywords.contains(keyword)) { + keywords.add(keyword); + } + } + } + + public void setKeywords(String items) { + addKeywords(items); + } + + public void addKeywords(String items) { + if (null != items) { + addKeywords(XMLWriter.unflattenList(items)); + } + } + + public void addKeywords(String[] ra) { + if (null != ra) { + for (int i = 0; i < ra.length; i++) { + addKeyword(ra[i]); + } + } + } + + public ArrayList getKeywordsList() { + return makeList(keywords); + } + + // ------- options - String args + + /** @return ArrayList of String options */ + public ArrayList getOptionsList() { + return makeList(options); + } + + /** @return String[] of options */ + public String[] getOptionsArray() { + return (String[]) options.toArray(new String[0]); + } + + public void setOption(String option) { + addOption(option); + } + + public void addOption(String option) { + if ((null != option) && (0 < option.length())) { + options.add(option); + } + } + + /** add options (from XML/bean) - removes any existing options */ + public void setOptions(String items) { + this.options.clear(); + addOptions(items); + } + + /** + * Set options, removing any existing options. + * + * @param options String[] options to use - may be null or empty + */ + public void setOptionsArray(String[] options) { + this.options.clear(); + if (!LangUtil.isEmpty(options)) { + this.options.addAll(Arrays.asList(options)); + } + } + + public void addOptions(String items) { + if (null != items) { + addOptions(XMLWriter.unflattenList(items)); + } + } + + public void addOptions(String[] ra) { + if (null != ra) { + for (int i = 0; i < ra.length; i++) { + addOption(ra[i]); + } + } + } + + // --------------- (String) paths + /** @return ArrayList of String paths */ + public ArrayList getPathsList() { + return makeList(paths); + } + + /** @return String[] of paths */ + public String[] getPathsArray() { + return (String[]) paths.toArray(new String[0]); + } + + public void setPath(String path) { + addPath(path); + } + + public void setPaths(String paths) { + addPaths(paths); + } + + public void addPath(String path) { + if (null != path) { + paths.add(path); + } + } + + public void addPaths(String items) { + if (null != items) { + addPaths(XMLWriter.unflattenList(items)); + } + } + + public void addPaths(String[] ra) { + if (null != ra) { + for (int i = 0; i < ra.length; i++) { + addPath(ra[i]); + } + } + } + + // --------------------- dir changes + public void addDirChanges(DirChanges.Spec dirChangesSpec) { + if (null != dirChangesSpec) { + dirChanges.add(dirChangesSpec); + } + } + + // --------------------- messages + public void setMessage(String message) { + addMessage(message); + } + + public void addMessage(IMessage message) { + if (null != message) { + if (!messages.handleMessage(message)) { + String s = "invalid message: " + message; + throw new IllegalArgumentException(s); + } + } + } + + public void addMessage(String message) { + if (null != message) { + addMessage(BridgeUtil.readMessage(message)); + } + } + + /** + * this can ONLY work if each item has no internal comma + */ + public void addMessages(String items) { + if (null != items) { + String[] ra = XMLWriter.unflattenList(items); + for (int i = 0; i < ra.length; i++) { + addMessage(ra[i]); + } + } + } + + public void addMessages(List messages) { + if (null != messages) { + for (Iterator iter = messages.iterator(); iter.hasNext();) { Object o = iter.next(); - if (o instanceof IMessage) { - addMessage((IMessage) o); - } else { - String m = "not message: " + o; - addMessage(new Message(m,IMessage.WARNING, null, null)); - } + if (o instanceof IMessage) { + addMessage((IMessage) o); + } else { + String m = "not message: " + o; + addMessage(new Message(m, IMessage.WARNING, null, null)); + } + } + } + } + + /** @return int number of message of this kind (optionally or greater */ + public int numMessages(IMessage.Kind kind, boolean orGreater) { + return messages.numMessages(kind, orGreater); + } + + public IMessageHolder getMessages() { + return messages; + } + + public void addChild(IRunSpec child) { + // fyi, child is added when complete (depth-first), not when initialized, + // so cannot affect initialization of child here + if (null != child) { + children.add(child); + } + } + + /** @return copy of children list */ + public ArrayList getChildren() { + return makeList(children); + } + + /** @return copy of children list without children to skip */ + public ArrayList getWorkingChildren() { + if (skipAll) { + return new ArrayList(); + } + if (null == skipSet) { + return getChildren(); + } + ArrayList result = new ArrayList(); + int i = 0; + for (Iterator iter = children.listIterator(); iter.hasNext(); i++) { + Object child = iter.next(); + if (!skipSet.get(i)) { + result.add(child); + } + } + return result; + } + + /** + * Recursively absorb parent values if different. This implementation calls doAdoptParentValues(..) and then calls this for any + * children. This is when skipped children are determined. Children may elect to balk at this point, reducing the number of + * children or causing this spec to skip if skipIfAnyChildrenSkipped. For each test skipped, either this doAdoptParentValues(..) + * or the child's adoptParentValues(..) should add one info message with the reason this is being skipped. The only reason to + * override this would be to NOT invoke the same for children, or to do something similar for children which are not + * AbstractRunSpec. + * + * @param parentRuntime the RT values to adopt - ignored if null + * @param handler the IMessageHandler for info messages when skipping + * @return false if this wants to be skipped, true otherwise + */ + public boolean adoptParentValues(RT parentRuntime, IMessageHandler handler) { + boolean skipped = false; + skipAll = false; + skipSet = new BitSet(); + if (null != parentRuntime) { + skipped = !doAdoptParentValues(parentRuntime, handler); + if (skipped && skipIfAnyChildSkipped) { // no need to continue checking + skipAll = true; + return false; } - } - } - - /** @return int number of message of this kind (optionally or greater */ - public int numMessages(IMessage.Kind kind, boolean orGreater) { - return messages.numMessages(kind, orGreater); - } - - public IMessageHolder getMessages() { - return messages; - } - - - public void addChild(IRunSpec child) { - // fyi, child is added when complete (depth-first), not when initialized, - // so cannot affect initialization of child here - if (null != child) { - children.add(child); - } - } - - /** @return copy of children list */ - public ArrayList getChildren() { - return makeList(children); - } - - /** @return copy of children list without children to skip */ - public ArrayList getWorkingChildren() { - if (skipAll) { - return new ArrayList(); - } - if (null == skipSet) { - return getChildren(); - } - ArrayList result = new ArrayList(); - int i = 0; - for (Iterator iter = children.listIterator(); - iter.hasNext(); i++) { - Object child = iter.next(); - if (!skipSet.get(i)) { - result.add(child); - } - } - return result; - } - - /** - * Recursively absorb parent values if different. - * This implementation calls doAdoptParentValues(..) - * and then calls this for any children. - * This is when skipped children are determined. - * Children may elect to balk at this point, reducing the - * number of children or causing this spec to skip if - * skipIfAnyChildrenSkipped. For each test skipped, either - * this doAdoptParentValues(..) or the child's adoptParentValues(..) - * should add one info message with the reason this is being skipped. - * The only reason to override this would be to NOT - * invoke the same for children, or to do something similar - * for children which are not AbstractRunSpec. - * @param parentRuntime the RT values to adopt - ignored if null - * @param handler the IMessageHandler for info messages when skipping - * @return false if this wants to be skipped, true otherwise - */ - public boolean adoptParentValues(RT parentRuntime, IMessageHandler handler) { - boolean skipped = false; - skipAll = false; - skipSet = new BitSet(); - if (null != parentRuntime) { - skipped = !doAdoptParentValues(parentRuntime, handler); - if (skipped && skipIfAnyChildSkipped) { // no need to continue checking - skipAll = true; - return false; - } - int i = 0; - for (ListIterator iter = children.listIterator(); iter.hasNext(); i++) { + int i = 0; + for (ListIterator iter = children.listIterator(); iter.hasNext(); i++) { IRunSpec child = (IRunSpec) iter.next(); if (child instanceof AbstractRunSpec) { - AbstractRunSpec arsChild = (AbstractRunSpec) child; - if (!arsChild.adoptParentValues(runtime, handler)) { - skipSet.set(i); - if (!skipped) { - skipped = true; - if (skipIfAnyChildSkipped) { // no need to continue checking - skipAll = true; - return false; - } - } - } - } + AbstractRunSpec arsChild = (AbstractRunSpec) child; + if (!arsChild.adoptParentValues(runtime, handler)) { + skipSet.set(i); + if (!skipped) { + skipped = true; + if (skipIfAnyChildSkipped) { // no need to continue checking + skipAll = true; + return false; + } + } + } + } } - } - return true; - } - - /** - * Adopt parent values. - * This implementation makes a local copy. - * If we interpret (and absorb) any options, they should be removed - * from parentRuntime. - * This sets verbose if different (override) - * and directly adopts parentOptions if ours is null - * and otherwise adds any non-null options we don't already have. - * setting verbose and adding to parent options. - * Implementors override this to affect how parent values are adopted. - * Implementors should not recurse into children. - * This method may be called multiple times, so implementors - * should not destroy any spec information. - * Always add an info message when returning false to skip - * @param parentRuntime the RT values to adopt - never null - * @return false if this wants to be skipped, true otherwise - */ - protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) { - if (runtime.verbose != parentRuntime.verbose) { - runtime.verbose = parentRuntime.verbose; - } - if (!LangUtil.isEmpty(runtime.parentOptions)) { - runtime.parentOptions.clear(); - } - if (!LangUtil.isEmpty(parentRuntime.parentOptions)) { - runtime.parentOptions.addAll(parentRuntime.parentOptions); - } - return true; - } - - /** - * Implementations call this when signalling skips to ensure consistency - * in message formatting - * @param handler the IMessageHandler sink - not null - * @param reason the String reason to skip - not null - */ - protected void skipMessage(IMessageHandler handler, String reason) { - LangUtil.throwIaxIfNull(handler, "handler"); - LangUtil.throwIaxIfNull(handler, "reason"); - // XXX for Runs, label does not identify the test - String label = toString(); - MessageUtil.info(handler, "skipping \"" + label + "\" because " + reason); - } - - // --------------------------- writing xml - would prefer castor.. - - /** - * Control XML output by renaming or suppressing output for - * attributes and subelements. - * Subelements are skipped by setting the XMLNames booleans to false. - * Attributes are skipped by setting their name to null. - * @param names XMLNames with new names and/or suppress flags. - */ - protected void setXMLNames(XMLNames names) { - if (null != names) { - xmlNames = names; - } - } - -// /** @return null if value is null or name="{value}" otherwise */ -// private String makeAttr(XMLWriter out, String name, String value) { -// if (null == value) { -// return null; -// } -// return XMLWriter.makeAttribute(name, value); -// } -// -// /** @return null if list is null or empty or name="{flattenedList}" otherwise */ -// private String makeAttr(XMLWriter out, String name, List list) { -// if (LangUtil.isEmpty(list)) { -// return null; -// } -// String flat = XMLWriter.flattenList(list); -// return XMLWriter.makeAttribute(name, flat); -// } -// - /** @return true if writeAttributes(..) will produce any output */ - protected boolean haveAttributes() { - return ((!LangUtil.isEmpty(xmlNames.descriptionName) - && !LangUtil.isEmpty(description)) - || (!LangUtil.isEmpty(xmlNames.keywordsName) - && !LangUtil.isEmpty(keywords)) - || (!LangUtil.isEmpty(xmlNames.optionsName) - && !LangUtil.isEmpty(options)) - || (!LangUtil.isEmpty(xmlNames.pathsName) - && !LangUtil.isEmpty(paths))); - } - - /** - * Write attributes without opening or closing elements/attributes. - * An attribute is written only if the value is not empty - * and the name in xmlNames is not empty - */ - protected void writeAttributes(XMLWriter out) { - if (!LangUtil.isEmpty(xmlNames.descriptionName) - && !LangUtil.isEmpty(description)) { - out.printAttribute(xmlNames.descriptionName, description); - } - if (!LangUtil.isEmpty(xmlNames.keywordsName) - && !LangUtil.isEmpty(keywords)) { - out.printAttribute(xmlNames.keywordsName, - XMLWriter.flattenList(keywords)); - } - if (!LangUtil.isEmpty(xmlNames.optionsName) - && !LangUtil.isEmpty(options)) { - out.printAttribute(xmlNames.optionsName, XMLWriter.flattenList(options)); - } - if (!LangUtil.isEmpty(xmlNames.pathsName) - && !LangUtil.isEmpty(paths)) { - out.printAttribute(xmlNames.pathsName, XMLWriter.flattenList(paths)); - } - if (!LangUtil.isEmpty(xmlNames.commentName) - && !LangUtil.isEmpty(comment)) { - out.printAttribute(xmlNames.commentName, comment); - } - if (isStaging && !LangUtil.isEmpty(xmlNames.stagingName)) { - out.printAttribute(xmlNames.stagingName, "true"); - } - if (badInput && !LangUtil.isEmpty(xmlNames.badInputName)) { - out.printAttribute(xmlNames.badInputName, "true"); - } - } - - /** - * The default implementation writes everything as attributes, - * then subelements for dirChanges, messages, then subelements for children. - * Subclasses that override may delegate back for any of these. - * Subclasses may also set XMLNames to name or suppress any attribute - * or subelement. - * @see writeMessages(XMLWriter) - * @see writeChildren(XMLWriter) - * @see IXmlWritable#writeXml(XMLWriter) - */ - public void writeXml(XMLWriter out) { - out.startElement(xmlElementName,false); - writeAttributes(out); - out.endAttributes(); - if (!xmlNames.skipMessages) { - writeMessages(out); - } - if (!xmlNames.skipChildren) { - writeChildren(out); - } - out.endElement(xmlElementName); - } + } + return true; + } + + /** + * Adopt parent values. This implementation makes a local copy. If we interpret (and absorb) any options, they should be removed + * from parentRuntime. This sets verbose if different (override) and directly adopts parentOptions if ours is null and otherwise + * adds any non-null options we don't already have. setting verbose and adding to parent options. Implementors override this to + * affect how parent values are adopted. Implementors should not recurse into children. This method may be called multiple + * times, so implementors should not destroy any spec information. Always add an info message when returning false to skip + * + * @param parentRuntime the RT values to adopt - never null + * @return false if this wants to be skipped, true otherwise + */ + protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) { + if (runtime.verbose != parentRuntime.verbose) { + runtime.verbose = parentRuntime.verbose; + } + if (!LangUtil.isEmpty(runtime.parentOptions)) { + runtime.parentOptions.clear(); + } + if (!LangUtil.isEmpty(parentRuntime.parentOptions)) { + runtime.parentOptions.addAll(parentRuntime.parentOptions); + } + return true; + } + + /** + * Implementations call this when signalling skips to ensure consistency in message formatting + * + * @param handler the IMessageHandler sink - not null + * @param reason the String reason to skip - not null + */ + protected void skipMessage(IMessageHandler handler, String reason) { + LangUtil.throwIaxIfNull(handler, "handler"); + LangUtil.throwIaxIfNull(handler, "reason"); + // XXX for Runs, label does not identify the test + String label = toString(); + MessageUtil.info(handler, "skipping \"" + label + "\" because " + reason); + } + + // --------------------------- writing xml - would prefer castor.. + + /** + * Control XML output by renaming or suppressing output for attributes and subelements. Subelements are skipped by setting the + * XMLNames booleans to false. Attributes are skipped by setting their name to null. + * + * @param names XMLNames with new names and/or suppress flags. + */ + protected void setXMLNames(XMLNames names) { + if (null != names) { + xmlNames = names; + } + } + + // /** @return null if value is null or name="{value}" otherwise */ + // private String makeAttr(XMLWriter out, String name, String value) { + // if (null == value) { + // return null; + // } + // return XMLWriter.makeAttribute(name, value); + // } + // + // /** @return null if list is null or empty or name="{flattenedList}" otherwise */ + // private String makeAttr(XMLWriter out, String name, List list) { + // if (LangUtil.isEmpty(list)) { + // return null; + // } + // String flat = XMLWriter.flattenList(list); + // return XMLWriter.makeAttribute(name, flat); + // } + // + /** @return true if writeAttributes(..) will produce any output */ + protected boolean haveAttributes() { + return ((!LangUtil.isEmpty(xmlNames.descriptionName) && !LangUtil.isEmpty(description)) + || (!LangUtil.isEmpty(xmlNames.keywordsName) && !LangUtil.isEmpty(keywords)) + || (!LangUtil.isEmpty(xmlNames.optionsName) && !LangUtil.isEmpty(options)) || (!LangUtil + .isEmpty(xmlNames.pathsName) && !LangUtil.isEmpty(paths))); + } + + /** + * Write attributes without opening or closing elements/attributes. An attribute is written only if the value is not empty and + * the name in xmlNames is not empty + */ + protected void writeAttributes(XMLWriter out) { + if (!LangUtil.isEmpty(xmlNames.descriptionName) && !LangUtil.isEmpty(description)) { + out.printAttribute(xmlNames.descriptionName, description); + } + if (!LangUtil.isEmpty(xmlNames.keywordsName) && !LangUtil.isEmpty(keywords)) { + out.printAttribute(xmlNames.keywordsName, XMLWriter.flattenList(keywords)); + } + if (!LangUtil.isEmpty(xmlNames.optionsName) && !LangUtil.isEmpty(options)) { + out.printAttribute(xmlNames.optionsName, XMLWriter.flattenList(options)); + } + if (!LangUtil.isEmpty(xmlNames.pathsName) && !LangUtil.isEmpty(paths)) { + out.printAttribute(xmlNames.pathsName, XMLWriter.flattenList(paths)); + } + if (!LangUtil.isEmpty(xmlNames.commentName) && !LangUtil.isEmpty(comment)) { + out.printAttribute(xmlNames.commentName, comment); + } + if (isStaging && !LangUtil.isEmpty(xmlNames.stagingName)) { + out.printAttribute(xmlNames.stagingName, "true"); + } + if (badInput && !LangUtil.isEmpty(xmlNames.badInputName)) { + out.printAttribute(xmlNames.badInputName, "true"); + } + } /** - * Write messages. Assumes attributes are closed, - * can write child elements of current element. + * The default implementation writes everything as attributes, then subelements for dirChanges, messages, then subelements for + * children. Subclasses that override may delegate back for any of these. Subclasses may also set XMLNames to name or suppress + * any attribute or subelement. + * + * @see writeMessages(XMLWriter) + * @see writeChildren(XMLWriter) + * @see IXmlWritable#writeXml(XMLWriter) + */ + public void writeXml(XMLWriter out) { + out.startElement(xmlElementName, false); + writeAttributes(out); + out.endAttributes(); + if (!xmlNames.skipMessages) { + writeMessages(out); + } + if (!xmlNames.skipChildren) { + writeChildren(out); + } + out.endElement(xmlElementName); + } + + /** + * Write messages. Assumes attributes are closed, can write child elements of current element. */ protected void writeMessages(XMLWriter out) { - if (0 < messages.numMessages(null, true)) { - SoftMessage.writeXml(out, messages); - } - - } - - /** - * Write children. Assumes attributes are closed, - * can write child elements of current element. - */ - protected void writeChildren(XMLWriter out) { - if (0 < children.size()) { - for (Iterator iter = children.iterator(); iter.hasNext();) { - IXmlWritable self = (IXmlWritable) iter.next(); - self.writeXml(out); - } - } - } - - // --------------------------- logging - - public void printAll(PrintStream out, String prefix) { - out.println(prefix + toString()); - for (Iterator iter = children.iterator(); iter.hasNext();) { + if (0 < messages.numMessages(null, true)) { + SoftMessage.writeXml(out, messages); + } + + } + + /** + * Write children. Assumes attributes are closed, can write child elements of current element. + */ + protected void writeChildren(XMLWriter out) { + if (0 < children.size()) { + for (Iterator iter = children.iterator(); iter.hasNext();) { + IXmlWritable self = (IXmlWritable) iter.next(); + self.writeXml(out); + } + } + } + + // --------------------------- logging + + public void printAll(PrintStream out, String prefix) { + out.println(prefix + toString()); + for (Iterator iter = children.iterator(); iter.hasNext();) { AbstractRunSpec child = (AbstractRunSpec) iter.next(); // IRunSpec - child.printAll(out, prefix + " "); - } - } - - /** - * default implementation returns the description if not empty - * or the unqualified class name otherwise. - * Subclasses should not call toString from here unless they reimplement it. - * @return name of this thing or type - */ - protected String getPrintName() { - if (!LangUtil.isEmpty(description)) { - return description; - } else { - return LangUtil.unqualifiedClassName(this); - } - } - - /** @return summary count of spec elements */ - public String toString() { - return getPrintName() + "(" + containedSummary() + ")"; - } - - /** @return String of the form (# [options|paths|locations|messages]).. */ - protected String containedSummary() { - StringBuffer result = new StringBuffer(); - addListCount("options", options, result); - addListCount("paths", paths, result); - //XXXXXunused addListCount("sourceLocations", sourceLocations, result); - List messagesList = messages.getUnmodifiableListView(); - addListCount("messages", messagesList, result); - - return result.toString().trim(); - } - - - public String toLongString() { - String mssg = ""; - if (0 < messages.numMessages(null, true)) { - mssg = " expected messages (" + MessageUtil.renderCounts(messages) + ")"; - } - return getPrintName() + containedToLongString() + mssg.trim(); - } - - /** @return String of the form (# [options|paths|locations|messages]).. */ - protected String containedToLongString() { - StringBuffer result = new StringBuffer(); - addListEntries("options", options, result); - addListEntries("paths", paths, result); -// XXXXXunused addListEntries("sourceLocations", sourceLocations, result); - List messagesList = messages.getUnmodifiableListView(); - addListEntries("messages", messagesList, result); - - return result.toString(); - } - - protected void initClone(AbstractRunSpec spec) - throws CloneNotSupportedException { - /* - * clone associated objects only if not (used as?) read-only. - */ - spec.badInput = badInput; - spec.children.clear(); - for (Iterator iter = children.iterator(); iter.hasNext();) { - // clone these... - IRunSpec child = (IRunSpec) iter.next(); - // require all child classes to support clone? - if (child instanceof AbstractRunSpec) { - spec.addChild((AbstractRunSpec) ((AbstractRunSpec) child).clone()); - } else { - throw new Error("unable to clone " + child); - } - } - spec.comment = comment; - spec.description = description; - spec.dirChanges.clear(); - spec.dirChanges.addAll(dirChanges); - spec.isStaging = spec.isStaging; - spec.keywords.clear(); - spec.keywords.addAll(keywords); - spec.messages.clearMessages(); - MessageUtil.handleAll(spec.messages, messages, false); - spec.options.clear(); - spec.options.addAll(options); - spec.paths.clear(); - spec.paths.addAll(paths); - spec.runtime.copy(runtime); - spec.skipAll = skipAll; - spec.skipIfAnyChildSkipped = skipIfAnyChildSkipped; - if (null != skipSet) { - spec.skipSet = new BitSet(); - spec.skipSet.or(skipSet); - } - //spec.sourceLocation = sourceLocation; - //spec.sourceLocations.clear(); -// XXXXXunused spec.sourceLocations.addAll(sourceLocations); - spec.xmlElementName = xmlElementName; - spec.xmlNames = ((AbstractRunSpec.XMLNames) xmlNames.clone()); - } - - private static void addListCount(String name, List list, StringBuffer sink) { - int size = list.size(); - if ((null != list) && (0 < size)) { - sink.append(" " + size + " "); - sink.append(name); - } - } - - private static void addListEntries(String name, List list, StringBuffer sink) { - if ((null != list) && (0 < list.size())) { - sink.append(" " + list.size() + " "); - sink.append(name); - sink.append(": "); - sink.append(list.toString()); - } - } - private ArrayList makeList(List list) { - ArrayList result = new ArrayList(); - if (null != list) { - result.addAll(list); - } - return result; - } - - /** - * Subclasses use this to rename attributes or omit attributes or subelements. - * To suppress output of an attribute, pass "" as the name of the attribute. - * To use default entries, pass null for that entry. - * XXX this really should be replaced with nested properties - * associated logical name with actual name (or placeholders - * for "unused" and "default"). - */ - public static class XMLNames { - public static final XMLNames DEFAULT = - new XMLNames(null, "description", "sourceLocation", - "keywords", "options", "paths", "comment", - "staging", "badInput", false, false, false); - final String descriptionName; - final String sourceLocationName; - final String keywordsName; - final String optionsName; - final String pathsName; - final String commentName; - final String stagingName; - final String badInputName; - final boolean skipDirChanges; - final boolean skipMessages; - final boolean skipChildren; - protected Object clone() { - return new XMLNames( - null, - descriptionName, - sourceLocationName, - keywordsName, - optionsName, - pathsName, - commentName, - stagingName, - badInputName, - skipDirChanges, - skipMessages, - skipChildren); - } - - // not runtime, skipAll, skipIfAnyChildSkipped, skipSet - // sourceLocations - /** reset all names/behavior or pass defaultNames - * as the defaults for any null elements - */ - XMLNames(XMLNames defaultNames, - String descriptionName, - String sourceLocationName, - String keywordsName, - String optionsName, - String pathsName, - String commentName, - String stagingName, - String badInputName, - boolean skipDirChanges, - boolean skipMessages, - boolean skipChildren) { - this.skipDirChanges = skipDirChanges; - this.skipMessages = skipMessages; - this.skipChildren = skipChildren; - if (null != defaultNames) { - this.descriptionName = (null != descriptionName? descriptionName : defaultNames.descriptionName); - this.sourceLocationName = (null != sourceLocationName ? sourceLocationName : defaultNames.sourceLocationName); - this.keywordsName = (null != keywordsName ? keywordsName : defaultNames.keywordsName); - this.optionsName = (null != optionsName ? optionsName : defaultNames.optionsName); - this.pathsName = (null != pathsName ? pathsName : defaultNames.pathsName); - this.commentName = (null != commentName ? commentName : defaultNames.commentName); - this.stagingName = (null != stagingName ? stagingName : defaultNames.stagingName); - this.badInputName = (null != badInputName ? badInputName : defaultNames.badInputName); - } else { - this.descriptionName = descriptionName; - this.sourceLocationName = sourceLocationName; - this.keywordsName = keywordsName; - this.optionsName = optionsName; - this.pathsName = pathsName; - this.commentName = commentName; - this.stagingName = stagingName; - this.badInputName = badInputName; - } - } - } - - /** subclasses implement this to create and set up a run */ - abstract public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator); - - /** segregate runtime-only state in spec */ - public static class RT { - /** true if we should emit verbose messages */ - private boolean verbose; - - /** null unless parent set options for children to consider */ - final private ArrayList /*String*/ parentOptions; - - public RT() { - parentOptions = new ArrayList(); - } - - public boolean isVerbose() { - return verbose; - } - - /** - * Set parent options - old options destroyed. - * Will result in duplicates if duplicates added. - * Null or empty entries are ignored - * @param options ignored if null or empty - */ - public void setOptions(String[] options) { - parentOptions.clear(); - if (!LangUtil.isEmpty(options)) { - for (int i = 0; i < options.length; i++) { - if (!LangUtil.isEmpty(options[i])) { - parentOptions.add(options[i]); - } + child.printAll(out, prefix + " "); + } + } + + /** + * default implementation returns the description if not empty or the unqualified class name otherwise. Subclasses should not + * call toString from here unless they reimplement it. + * + * @return name of this thing or type + */ + protected String getPrintName() { + if (!LangUtil.isEmpty(description)) { + return description; + } else { + return LangUtil.unqualifiedClassName(this); + } + } + + /** @return summary count of spec elements */ + public String toString() { + return getPrintName() + "(" + containedSummary() + ")"; + } + + /** @return String of the form (# [options|paths|locations|messages]).. */ + protected String containedSummary() { + StringBuffer result = new StringBuffer(); + addListCount("options", options, result); + addListCount("paths", paths, result); + // XXXXXunused addListCount("sourceLocations", sourceLocations, result); + List messagesList = messages.getUnmodifiableListView(); + addListCount("messages", messagesList, result); + + return result.toString().trim(); + } + + public String toLongString() { + String mssg = ""; + if (0 < messages.numMessages(null, true)) { + mssg = " expected messages (" + MessageUtil.renderCounts(messages) + ")"; + } + return getPrintName() + containedToLongString() + mssg.trim(); + } + + /** @return String of the form (# [options|paths|locations|messages]).. */ + protected String containedToLongString() { + StringBuffer result = new StringBuffer(); + addListEntries("options", options, result); + addListEntries("paths", paths, result); + // XXXXXunused addListEntries("sourceLocations", sourceLocations, result); + List messagesList = messages.getUnmodifiableListView(); + addListEntries("messages", messagesList, result); + + return result.toString(); + } + + protected void initClone(AbstractRunSpec spec) throws CloneNotSupportedException { + /* + * clone associated objects only if not (used as?) read-only. + */ + spec.badInput = badInput; + spec.children.clear(); + for (Iterator iter = children.iterator(); iter.hasNext();) { + // clone these... + IRunSpec child = (IRunSpec) iter.next(); + // require all child classes to support clone? + if (child instanceof AbstractRunSpec) { + spec.addChild((AbstractRunSpec) ((AbstractRunSpec) child).clone()); + } else { + throw new Error("unable to clone " + child); + } + } + spec.comment = comment; + spec.description = description; + spec.dirChanges.clear(); + spec.dirChanges.addAll(dirChanges); + spec.isStaging = spec.isStaging; + spec.keywords.clear(); + spec.keywords.addAll(keywords); + spec.messages.clearMessages(); + MessageUtil.handleAll(spec.messages, messages, false); + spec.options.clear(); + spec.options.addAll(options); + spec.paths.clear(); + spec.paths.addAll(paths); + spec.runtime.copy(runtime); + spec.skipAll = skipAll; + spec.skipIfAnyChildSkipped = skipIfAnyChildSkipped; + if (null != skipSet) { + spec.skipSet = new BitSet(); + spec.skipSet.or(skipSet); + } + // spec.sourceLocation = sourceLocation; + // spec.sourceLocations.clear(); + // XXXXXunused spec.sourceLocations.addAll(sourceLocations); + spec.xmlElementName = xmlElementName; + spec.xmlNames = ((AbstractRunSpec.XMLNames) xmlNames.clone()); + } + + private static void addListCount(String name, List list, StringBuffer sink) { + int size = list.size(); + if ((null != list) && (0 < size)) { + sink.append(" " + size + " "); + sink.append(name); + } + } + + private static void addListEntries(String name, List list, StringBuffer sink) { + if ((null != list) && (0 < list.size())) { + sink.append(" " + list.size() + " "); + sink.append(name); + sink.append(": "); + sink.append(list.toString()); + } + } + + private ArrayList makeList(List list) { + ArrayList result = new ArrayList(); + if (null != list) { + result.addAll(list); + } + return result; + } + + /** + * Subclasses use this to rename attributes or omit attributes or subelements. To suppress output of an attribute, pass "" as + * the name of the attribute. To use default entries, pass null for that entry. XXX this really should be replaced with nested + * properties associated logical name with actual name (or placeholders for "unused" and "default"). + */ + public static class XMLNames { + public static final XMLNames DEFAULT = new XMLNames(null, "description", "sourceLocation", "keywords", "options", "paths", + "comment", "staging", "badInput", false, false, false); + final String descriptionName; + final String sourceLocationName; + final String keywordsName; + final String optionsName; + final String pathsName; + final String commentName; + final String stagingName; + final String badInputName; + final boolean skipDirChanges; + final boolean skipMessages; + final boolean skipChildren; + + protected Object clone() { + return new XMLNames(null, descriptionName, sourceLocationName, keywordsName, optionsName, pathsName, commentName, + stagingName, badInputName, skipDirChanges, skipMessages, skipChildren); + } + + // not runtime, skipAll, skipIfAnyChildSkipped, skipSet + // sourceLocations + /** + * reset all names/behavior or pass defaultNames as the defaults for any null elements + */ + XMLNames(XMLNames defaultNames, String descriptionName, String sourceLocationName, String keywordsName, String optionsName, + String pathsName, String commentName, String stagingName, String badInputName, boolean skipDirChanges, + boolean skipMessages, boolean skipChildren) { + this.skipDirChanges = skipDirChanges; + this.skipMessages = skipMessages; + this.skipChildren = skipChildren; + if (null != defaultNames) { + this.descriptionName = (null != descriptionName ? descriptionName : defaultNames.descriptionName); + this.sourceLocationName = (null != sourceLocationName ? sourceLocationName : defaultNames.sourceLocationName); + this.keywordsName = (null != keywordsName ? keywordsName : defaultNames.keywordsName); + this.optionsName = (null != optionsName ? optionsName : defaultNames.optionsName); + this.pathsName = (null != pathsName ? pathsName : defaultNames.pathsName); + this.commentName = (null != commentName ? commentName : defaultNames.commentName); + this.stagingName = (null != stagingName ? stagingName : defaultNames.stagingName); + this.badInputName = (null != badInputName ? badInputName : defaultNames.badInputName); + } else { + this.descriptionName = descriptionName; + this.sourceLocationName = sourceLocationName; + this.keywordsName = keywordsName; + this.optionsName = optionsName; + this.pathsName = pathsName; + this.commentName = commentName; + this.stagingName = stagingName; + this.badInputName = badInputName; + } + } + } + + /** subclasses implement this to create and set up a run */ + abstract public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator); + + /** segregate runtime-only state in spec */ + public static class RT { + /** true if we should emit verbose messages */ + private boolean verbose; + + /** null unless parent set options for children to consider */ + final private ArrayList /* String */parentOptions; + + public RT() { + parentOptions = new ArrayList(); + } + + public boolean isVerbose() { + return verbose; + } + + /** + * Set parent options - old options destroyed. Will result in duplicates if duplicates added. Null or empty entries are + * ignored + * + * @param options ignored if null or empty + */ + public void setOptions(String[] options) { + parentOptions.clear(); + if (!LangUtil.isEmpty(options)) { + for (int i = 0; i < options.length; i++) { + if (!LangUtil.isEmpty(options[i])) { + parentOptions.add(options[i]); + } } - } - } - - /** - * Copy values from another RT - * @param toCopy the RT to copy from - * @throws IllegalArgumentException if toCopy is null - */ - public void copy(RT toCopy) { - LangUtil.throwIaxIfNull(toCopy, "parent"); - parentOptions.clear(); - parentOptions.addAll(toCopy.parentOptions); - verbose = toCopy.verbose; - } - - /** - * Return any parent option accepted by validOptions, - * optionally removing the parent option. - * @param validOptions String[] of options to extract - * @param remove if true, then remove any parent option matched - * @return String[] containing any validOptions[i] in parentOptions - * - */ - public Values extractOptions( - Options validOptions, - boolean remove, - StringBuffer errors) { - Values result = Values.EMPTY; - if (null == errors) { - errors = new StringBuffer(); - } - if (null == validOptions) { - errors.append("null options"); - return result; - } - if (LangUtil.isEmpty(parentOptions)) { - return result; - } -// boolean haveOption = false; - String[] parents = (String[]) parentOptions.toArray(new String[0]); - try { - result = validOptions.acceptInput(parents); - } catch (InvalidInputException e) { - errors.append(e.getFullMessage()); - return result; - } - if (remove) { - Option.Value[] values = result.asArray(); - for (int i = 0; i < values.length; i++) { - Option.Value value = values[i]; - if (null == value) { - continue; - } - final int max = i + value.option.numArguments(); - if (max > i) { - if (max >= parents.length) { - errors.append("expecting more args for " - + value.option - + " at [" - + i - + "]: " - + Arrays.asList(parents)); - return result; - } - // XXX verify - for (int j = i; j < max ; j++) { - parentOptions.remove(parents[j]); - } - i = max-1; - } - } - } - return result; - } - - /** - * Return any parent option which has one of validOptions as a prefix, - * optionally absorbing (removing) the parent option. - * @param validOptions String[] of options to extract - * @param absorb if true, then remove any parent option matched - * @return String[] containing any validOptions[i] in parentOptions - * (at most once) - */ - public String[] extractOptions(String[] validOptions, boolean absorb) { - if (LangUtil.isEmpty(validOptions) || LangUtil.isEmpty(parentOptions)) { - return new String[0]; - } - ArrayList result = new ArrayList(); -// boolean haveOption = false; - for (int i = 0; i < validOptions.length; i++) { + } + } + + /** + * Copy values from another RT + * + * @param toCopy the RT to copy from + * @throws IllegalArgumentException if toCopy is null + */ + public void copy(RT toCopy) { + LangUtil.throwIaxIfNull(toCopy, "parent"); + parentOptions.clear(); + parentOptions.addAll(toCopy.parentOptions); + verbose = toCopy.verbose; + } + + /** + * Return any parent option accepted by validOptions, optionally removing the parent option. + * + * @param validOptions String[] of options to extract + * @param remove if true, then remove any parent option matched + * @return String[] containing any validOptions[i] in parentOptions + * + */ + public Values extractOptions(Options validOptions, boolean remove, StringBuffer errors) { + Values result = Values.EMPTY; + if (null == errors) { + errors = new StringBuffer(); + } + if (null == validOptions) { + errors.append("null options"); + return result; + } + if (LangUtil.isEmpty(parentOptions)) { + return result; + } + // boolean haveOption = false; + String[] parents = (String[]) parentOptions.toArray(new String[0]); + try { + result = validOptions.acceptInput(parents); + } catch (InvalidInputException e) { + errors.append(e.getFullMessage()); + return result; + } + if (remove) { + Option.Value[] values = result.asArray(); + for (int i = 0; i < values.length; i++) { + Option.Value value = values[i]; + if (null == value) { + continue; + } + final int max = i + value.option.numArguments(); + if (max > i) { + if (max >= parents.length) { + errors.append("expecting more args for " + value.option + " at [" + i + "]: " + Arrays.asList(parents)); + return result; + } + // XXX verify + for (int j = i; j < max; j++) { + parentOptions.remove(parents[j]); + } + i = max - 1; + } + } + } + return result; + } + + /** + * Return any parent option which has one of validOptions as a prefix, optionally absorbing (removing) the parent option. + * + * @param validOptions String[] of options to extract + * @param absorb if true, then remove any parent option matched + * @return String[] containing any validOptions[i] in parentOptions (at most once) + */ + public String[] extractOptions(String[] validOptions, boolean absorb) { + if (LangUtil.isEmpty(validOptions) || LangUtil.isEmpty(parentOptions)) { + return new String[0]; + } + ArrayList result = new ArrayList(); + // boolean haveOption = false; + for (int i = 0; i < validOptions.length; i++) { String option = validOptions[i]; - if (LangUtil.isEmpty(option)) { - continue; - } - for (ListIterator iter = parentOptions.listIterator(); iter.hasNext();) { + if (LangUtil.isEmpty(option)) { + continue; + } + for (ListIterator iter = parentOptions.listIterator(); iter.hasNext();) { String parentOption = (String) iter.next(); if (parentOption.startsWith(option)) { - result.add(parentOption); - if (absorb) { - iter.remove(); - } - } + result.add(parentOption); + if (absorb) { + iter.remove(); + } + } } } - return (String[]) result.toArray(new String[0]); - } - - /** Get ListIterator that permits removals */ - ListIterator getListIterator() { - return parentOptions.listIterator(); - } - - /** + return (String[]) result.toArray(new String[0]); + } + + /** Get ListIterator that permits removals */ + ListIterator getListIterator() { + return parentOptions.listIterator(); + } + + /** * Enable verbose logging + * * @param verbose if true, do verbose logging - */ + */ public void setVerbose(boolean verbose) { - if (this.verbose != verbose) { - this.verbose = verbose; - } + if (this.verbose != verbose) { + this.verbose = verbose; + } } - } // class RT - + } // class RT + }