123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- /* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation,
- * 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Xerox/PARC initial implementation
- * Wes Isberg 2004 updates
- * ******************************************************************/
-
- package org.aspectj.testing.harness.bridge;
-
- import java.io.PrintStream;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.BitSet;
- import java.util.Iterator;
- import java.util.List;
- import java.util.ListIterator;
-
- import org.aspectj.bridge.IMessage;
- import org.aspectj.bridge.IMessageHandler;
- import org.aspectj.bridge.IMessageHolder;
- import org.aspectj.bridge.ISourceLocation;
- import org.aspectj.bridge.Message;
- import org.aspectj.bridge.MessageHandler;
- 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.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.
- * <p>
- * <u>initialization</u>: 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.
- * <p>
- * <u>subclassing</u>: 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.
- * <p>
- * <u>bean properties</u>: 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).
- * <p>
- * <u>Coordination with writers</u>: 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 */
- 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<String>();
- paths = new ArrayList<String>();
- // XXXXXunused sourceLocations = new ArrayList();
- keywords = new ArrayList<String>();
- children = new ArrayList<IRunSpec>();
- 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<String> getKeywordsList() {
- return makeList(keywords);
- }
-
- // ------- options - String args
-
- /** @return ArrayList of String options */
- public ArrayList<String> 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<String> 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));
- }
- }
- }
- }
-
- /** @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<IRunSpec> getChildren() {
- return makeList(children);
- }
-
- /** @return copy of children list without children to skip */
- public ArrayList<IRunSpec> getWorkingChildren() {
- if (skipAll) {
- return new ArrayList<IRunSpec>();
- }
- if (null == skipSet) {
- return getChildren();
- }
- ArrayList<IRunSpec> result = new ArrayList<IRunSpec>();
- int i = 0;
- for (Iterator<IRunSpec> iter = children.listIterator(); iter.hasNext(); i++) {
- IRunSpec 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++) {
- 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;
- }
- }
- }
- }
- }
- }
- 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);
- }
-
- /**
- * 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<IRunSpec> 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<IRunSpec> 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<IMessage> 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<IMessage> 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<IRunSpec> iter = children.iterator(); iter.hasNext();) {
- // clone these...
- IRunSpec child = 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 <T> ArrayList<T> makeList(List<T> list) {
- ArrayList<T> result = new ArrayList<T>();
- 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<String>();
- }
-
- 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<String> result = new ArrayList<String>();
- // boolean haveOption = false;
- for (int i = 0; i < validOptions.length; i++) {
- String option = validOptions[i];
- if (LangUtil.isEmpty(option)) {
- continue;
- }
- for (ListIterator<String> iter = parentOptions.listIterator(); iter.hasNext();) {
- String parentOption = iter.next();
- if (parentOption.startsWith(option)) {
- result.add(parentOption);
- if (absorb) {
- iter.remove();
- }
- }
- }
- }
- return (String[]) result.toArray(new String[0]);
- }
-
- /** Get ListIterator that permits removals */
- ListIterator<String> 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;
- }
- }
- } // class RT
-
- }
|