123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 |
- /* *******************************************************************
- * Copyright (c) 2003 Contributors.
- * 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:
- * Wes Isberg initial implementation
- * ******************************************************************/
-
- package org.aspectj.testing.util.options;
-
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.TreeMap;
-
- import org.aspectj.testing.util.options.Option.Value;
- import org.aspectj.util.LangUtil;
-
- /**
- * Wrapper for Value[] that handles search boilerplate.
- */
- public class Values {
- public static final Values EMPTY;
- /** used by methods taking Selector to halt processing early */
- private static final boolean VERIFYING = true;
- private static final boolean FIND_ALL = true;
-
- private static final String NO_ERROR = "no error";
-
- static {
- EMPTY = new Values(new Value[0]);
- }
-
- public static Values wrapValues(Value[] values) {
- if ((null == values) || (0 == values.length)) {
- return EMPTY;
- }
- return new Values(values);
- }
-
- public static Values wrapValues(Values[] values) {
- if ((null == values) || (0 == values.length)) {
- return EMPTY;
- }
- Value[] input = null;
- if (values.length == 1) {
- input = values[0].asArray();
- LangUtil.throwIaxIfNull(input, "values");
- } else {
- int length = 0;
- for (int i = 0; i < values.length; i++) {
- if (values[i] == null) {
- LangUtil.throwIaxIfNull(null, "null value[" + i + "]");
- }
- length += values[i].length();
- }
- input = new Value[length];
- length = 0;
- Value[] temp;
- for (int i = 0; i < values.length; i++) {
- temp = values[i].asArray();
- System.arraycopy(temp, 0, input, length, temp.length);
- length += temp.length;
- }
- }
- return new Values(input);
- }
-
- static int[] invert(int[] missed, int length) {
- final int MAX = length;
- final int len = MAX - missed.length;
- final int[] result = new int[len];
- int missedIndex = 0;
- int resultIndex = 0;
- for (int counter = 0; counter < MAX; counter++) {
- // catch result up to missed
- while (((missedIndex >= missed.length)
- || (missed[missedIndex] > counter))
- && (counter < MAX)) {
- result[resultIndex++] = counter++;
- }
- // absorb missed up to counter
- while ((missedIndex < missed.length)
- && (missed[missedIndex] <= counter)
- && (counter < MAX)) {
- missedIndex++;
- }
- }
- return result;
- }
-
- private static Option.Value[] toArray(ArrayList list) {
- return (Option.Value[]) list.toArray(new Option.Value[0]);
- }
-
- /**
- * Resolve input to remove any values matching the same options,
- * where option conflicts are handled by option forcing.
- * First, for any force-off value, all matching set-on and
- * the force-off itself are removed. At this time, if there
- * is a matching force-on, then this will return an error.
- * Next, for any force-on value, it is converted to set-on,
- * and any other matching set-on value is removed.
- * Finally, this signals a collision if two values share
- * the same option family and the family reports that this is
- * a collision.
- * In all cases, only the first error detected is reported.
- * @param input the Option.Value[] matched from the input,
- * (forced/duplicate options will be set to null)
- * @return String error during resolution, or null if no error
- */
- private static String resolve(Option.Value[] input) {
- String err = null;
- if (LangUtil.isEmpty(input)) {
- return null;
- }
-
- Map familyToMatches = new TreeMap();
- for (int i = 0;(null == err) && (i < input.length); i++) {
- if (null != input[i]) {
- Option.Family family = input[i].option.getFamily();
- int[] matches = (int[]) familyToMatches.get(family);
- if (null == matches) {
- matches = match(input, i);
- familyToMatches.put(family, matches);
- }
- }
- }
-
- familyToMatches = Collections.unmodifiableMap(familyToMatches);
- for (Iterator iter = familyToMatches.entrySet().iterator();
- (null == err) && iter.hasNext();
- ) {
- Map.Entry entry = (Map.Entry) iter.next();
- int[] matches = (int[]) entry.getValue();
- err = resolve(input, matches);
- }
- return err;
- }
-
- /**
- * Resolve all related options into one
- * by nullifying or modifying the values.
- *
- * First, for any force-off value,
- * remove all identical set-on
- * and the force-off itself,
- * and alert on any identical force-on.
- *
- * Next, for any force-on value,
- * convert to set-on,
- * throw Error on any same-family force-off value,
- * remove any identical force-on or set-on value,
- * alert on any other non-identical same-family force-on value,
- * remove any same-family set-on value,
- * and alert on any same-family set-off value.
- *
- * Finally, alert if any two remaining values share
- * the same option family, unless the option is marked
- * as permitting multiple values.
- *
- * @param input the Option.Value[] matching the input
- * @param matches the int[] list of indexes into input for
- * values for related by option
- * (all such values must have option matched by family)
- * @return String error, if any, or null if no error
- * @see #match(Option.Value[], int)
- */
- private static String resolve(Option.Value[] input, int[] matches) {
- String err = null;
- // seek force-off
- // Option.Value forceOff = null;
- Option option = null;
- // find and remove any force-off
- for (int i = 0;(null == err) && (i < matches.length); i++) {
- Option.Value value = input[matches[i]];
- if (null != value) {
- // verify that matches are in the same family
- if (VERIFYING) {
- if (null == option) {
- option = value.option;
- } else if (!(option.sameOptionFamily(value.option))) {
- String s =
- value.option
- + " has different family from "
- + option;
- throw new IllegalArgumentException(s);
- }
- }
- if (value.prefix.forceOff()) {
- err = removeForceOff(input, value, matches);
- }
- }
- }
- // find and set any force-on, removing others
- for (int i = 0;(null == err) && (i < matches.length); i++) {
- Option.Value value = input[matches[i]];
- if (null != value) {
- if (value.prefix.forceOn()) {
- err = convertForceOn(input, i, matches);
- }
- }
- }
- // remove any exact duplicates
- for (int i = 0;(null == err) && (i < matches.length); i++) {
- Option.Value value = input[matches[i]];
- if (null != value) {
- for (int j = i + 1; j < matches.length; j++) {
- if (value.sameValueIdentifier(input[matches[j]])) {
- input[matches[j]] = null;
- }
- }
- }
- }
- // signal error if two left unless permitMultipleFamilyValues
- Option.Value first = null;
- for (int i = 0;(null == err) && (i < matches.length); i++) {
- Option.Value value = input[matches[i]];
- if (null != value) {
- if (null == first) {
- first = value;
- if (first
- .option
- .getFamily()
- .permitMultipleFamilyValues()) {
- break;
- }
- } else {
- err = "collision between " + first + " and " + value;
- }
- }
- }
-
- return err;
- }
-
- /**
- * For any force-off value,
- * remove all set-on or force-off with same value
- * (including the force-off itself),
- * and alert on any identical force-on.
- * @param input the Option.Value[] matching the input
- * @param value the force-off Option.Value to remove
- * @param matches the int[] list of indexes into input for
- * values for related by option
- * (all such values must have matching option)
- * @return String error if any
- */
- private static String removeForceOff(
- Option.Value[] input,
- Option.Value value,
- int[] matches) {
- if (!value.prefix.forceOff()) {
- throw new IllegalArgumentException(
- "expecting force-off: " + value);
- }
- for (int i = 0; i < matches.length; i++) {
- Option.Value match = input[matches[i]];
- if ((null != match) && value.sameValueIdentifier(match)) {
- if (match.prefix.forceOn()) {
- return "force conflict between "
- + value
- + " and "
- + match;
- } else {
- input[matches[i]] = null; // unset matches[i]?
- }
- }
- }
- return null;
- }
-
- /**
- * For this force-on value, convert to set-on,
- * throw Error on any same-family force-off value,
- * remove any identical force-on or set-on value,
- * alert on any other non-identical same-family force-on value,
- * remove any same-family set-on value,
- * and alert on any same-family set-off value.
- * This must be called after <code>removeForceOff(..)</code>.
- * @param input the Option.Value[] to modify
- * @param valueIndex the int index in matches to find the force-on
- * and to start after
- * @param matches the int[] map into input entries with matching options
- * @return
- * @throw Error if any matching force-off found
- */
- private static String convertForceOn(
- Option.Value[] input,
- int valueIndex,
- int[] matches) {
- Option.Value value = input[matches[valueIndex]];
- if (!value.prefix.forceOn()) {
- throw new IllegalArgumentException(
- "expecting force-on: " + value);
- }
- input[matches[valueIndex]] = value.convert(Option.ON);
- for (int i = 0; i < matches.length; i++) {
- if (i == valueIndex) {
- continue;
- }
- Option.Value match = input[matches[i]];
- if (null != match) {
- // assert match.sameOptionFamily(value);
- if (match.prefix.forceOff()) {
- throw new Error(
- "unexpected force-off:"
- + match
- + " when processing "
- + value);
- }
- if (value.option.sameOptionIdentifier(match.option)) {
- input[matches[i]] = null;
- // remove any identical force-on or set
- } else if (match.prefix.forceOn()) {
- return "conflict between " + match + " and " + value;
- } else if (match.prefix.isSet()) {
- input[matches[i]] = null;
- // remove any same-value set-on value
- } else { // same family, force-off
- return "collision between " + match + " and " + value;
- }
- }
- }
- return null;
- }
-
- /**
- * Get a list of input matching the option in the initial value,
- * rendered as indexes into the input array.
- * @param input the Option.Value[] to seek in
- * @param start the int index of the starting position
- * @return int[] of indexes into input with the same option
- * as index[start] - never null, but can be empty
- */
- private static int[] match(Option.Value[] input, int start) {
- IntList result = new IntList();
- Option.Family key = null;
- Option.Family nextKey = null;
- for (int i = start; i < input.length; i++) {
- if (null != input[i]) {
- nextKey = input[i].option.getFamily();
- if (null == key) {
- key = nextKey;
- result.add(i);
- } else if (key.equals(nextKey)) {
- result.add(i);
- }
- }
- }
- return result.getList();
- }
-
- static int nullify(Option.Value[] values, Selector selector) {
- LangUtil.throwIaxIfNull(selector, "selector");
- int changed = 0;
- for (int i = 0; i < values.length; i++) {
- final boolean accepted;
- try {
- accepted = selector.accept(values[i]);
- } catch (Error e) {
- if (e != Selector.STOP) {
- throw e;
- }
- break;
- }
- if (accepted) {
- if (null != values[i]) {
- values[i] = null;
- changed++;
- }
- }
- }
- return changed;
- }
-
- /**
- * Render set values as String using associated prefix.
- * @param values the Value[] to render
- * @return String[] of values rendered for output
- * (never null or longer than values, but might be shorter)
- */
- private static String[] render(Value[] values) {
- ArrayList list = new ArrayList();
- for (int i = 0; i < values.length; i++) {
- if (null != values[i]) {
- String[] output = values[i].unflatten();
- if (LangUtil.isEmpty(output)) {
- throw new Error("no output for " + values[i]);
- }
-
- String s = values[i].prefix.render(output[0]);
- if (null != s) { // this means the prefix is set
- list.add(s);
- for (int j = 1; j < output.length; j++) {
- list.add(output[j]);
- }
- }
- }
- }
- return (String[]) list.toArray(new String[list.size()]);
- }
-
- private final Option.Value[] values;
- private Option.Value[] valuesNotNull;
- private String resolveError;
-
- private Values(Value[] values) {
- this.values = new Value[values.length];
- System.arraycopy(values, 0, this.values, 0, values.length);
- }
-
- public int length() {
- return values.length;
- }
-
- public Option.Value[] asArray() {
- Option.Value[] result = new Option.Value[values.length];
- System.arraycopy(values, 0, result, 0, result.length);
- return result;
- }
-
- /**
- * Emit as String[] the non-null values.
- * @return String[] of matched entries (never null, elements not null)
- */
- public String[] render() {
- return Values.render(valuesNotNull());
- }
-
- public String toString() {
- return Arrays.asList(values).toString();
- }
-
- /**
- * Create index into values of those that were matched,
- * including the options and their arguments.
- * @return int[] of elements in values that are not null (options)
- * or that represent option arguments
- */
- public int[] indexMatches() {
- // must be in order, low to high
- final int[] missed = indexMissedMatches();
- return invert(missed, length());
- }
-
- /**
- * Create index into values of missed input,
- * taking into account that matched arguments are
- * represented as null.
- * @return int[] of elements in values that are null
- * or optionally represent option arguments
- */
- public int[] indexMissedMatches() {
- MissedSelector selector = new MissedSelector();
- find(selector, FIND_ALL);
- String errors = selector.getErrors();
- if (null != errors) {
- throw new Error(errors);
- }
- return selector.getResult();
- }
-
- public Value firstInFamily(Option.Family family) {
- return findFirst(new ValueSelector(family));
- }
-
- public Value[] allInFamily(Option.Family family) {
- return find(new ValueSelector(family), FIND_ALL);
- }
-
- public Value firstOption(Option option) {
- return findFirst(new ValueSelector(option));
- }
-
- public Value[] allOption(Option option) {
- return find(new ValueSelector(option), FIND_ALL);
- }
-
- public Value firstValue(Option option, String value) {
- LangUtil.throwIaxIfNull(value, "value");
- return findFirst(new ValueSelector(option, value));
- }
-
- public Value[] allValues(Option option, String value) {
- LangUtil.throwIaxIfNull(value, "value");
- return find(new ValueSelector(option, value), FIND_ALL);
- }
-
- public boolean isResolved() {
- return ((this != EMPTY) && (null != resolveError));
- }
-
- /**
- *
- * @param selector the Selector to pick out entries to nullify
- * (should throw STOP to halt processing)
- * @return Values resulting from nullifying entries,
- * or this if none were changed
- */
- public Values nullify(Selector selector) {
- if (null == selector) {
- return this;
- }
- Value[] temp = asArray();
- int changed = nullify(temp, selector);
- if (0 == changed) {
- return this;
- }
- return new Values(temp);
- }
-
- /**
- * Resolve options, removing duplicates by force if necessary.
- * If any error is returned, then the values are left unchanged.
- * @return String error, if any
- * @throws IllegalStateException if <code>isResolved()</code>
- */
- public String resolve() {
- if (isResolved()) {
- throw new IllegalStateException("already resolved");
- }
- Option.Value[] temp = asArray();
- resolveError = resolve(temp);
- if (null == resolveError) {
- System.arraycopy(temp, 0, values, 0, temp.length);
- valuesNotNull = null;
- resolveError = NO_ERROR;
- return null;
- }
- return resolveError;
- }
-
- protected Option.Value findFirst(Selector filter) {
- Option.Value[] result = find(filter, !FIND_ALL);
- return (0 == result.length ? null : result[0]);
- }
-
- protected Option.Value[] find(Selector filter, boolean findAll) {
- LangUtil.throwIaxIfNull(filter, "filter");
- ArrayList result = new ArrayList();
- for (int i = 0; i < values.length; i++) {
- final boolean accepted;
- try {
- accepted = filter.accept(values[i]);
- } catch (Error e) {
- if (Selector.STOP != e) {
- throw e;
- }
- break;
- }
- if (accepted) {
- result.add(values[i]);
- if (findAll != FIND_ALL) {
- break;
- }
- }
- }
- return toArray(result);
- }
-
- private Option.Value[] valuesNotNull() {
- if (null == valuesNotNull) {
- ArrayList list = new ArrayList();
- for (int i = 0; i < this.values.length; i++) {
- if (null != this.values[i]) {
- list.add(this.values[i]);
- }
- }
- valuesNotNull = toArray(list);
- }
- return valuesNotNull;
- }
-
- public static class Selector {
- public static final Error STOP = new Error("stop invoking Selector");
- protected Selector() {
- }
- protected boolean accept(Value value) {
- return false;
- }
- }
- protected static class ValueSelector extends Selector {
-
- private final Option option;
- private final Option.Family family;
- private final String value;
- ValueSelector(Option.Family family) {
- LangUtil.throwIaxIfNull(family, "family");
- this.family = family;
- option = null;
- value = null;
- }
- ValueSelector(Option option) {
- this(option, (String) null);
- }
- ValueSelector(Option option, String value) {
- LangUtil.throwIaxIfNull(option, "option");
- this.option = option;
- family = null;
- this.value = value;
- }
- protected boolean accept(Value value) {
- if (null == value) {
- return false;
- }
- if (null != family) {
- return family.sameFamily(value.option.getFamily());
- } else if (!option.sameOptionIdentifier(value.option)) {
- return false;
- } else {
- return ((null == this.value)
- || (this.value.equals(value.value)));
- }
- }
- }
-
- /** pick all null entries (except for args), return as int[] */
- protected static class MissedSelector extends Selector {
- public static final String DELIM = "; ";
- final IntList result = new IntList();
- int index;
- final StringBuffer errors = new StringBuffer();
- int argsExpected;
- Option argsExpectedFor;
- MissedSelector() {
- }
-
- int[] getResult() {
- return result.getList();
- }
-
- /**
- * add index if value is null
- * unless skipArguments
- */
- protected boolean accept(Value value) {
- index++;
- if (null != value) {
- if (0 < argsExpected) { // expected more (null) args
- missedArgsFor(argsExpectedFor, argsExpected);
- }
- argsExpected = value.option.numArguments();
- argsExpectedFor = value.option;
- } else if (0 < argsExpected) { // ignore null in arg position
- argsExpected--;
- if (0 == argsExpected) {
- argsExpectedFor = null;
- }
- } else { // null, not expecting arg, so missing
- result.add(index - 1);
- return true;
- }
- return false;
- }
-
- private void missedArgsFor(Option option, int numArgs) {
- errors.append("missed ");
- errors.append(numArgs + " args for ");
- errors.append(option + DELIM);
- }
-
- String getErrors() {
- if (0 < argsExpected) {
- }
- if (0 == errors.length()) {
- return null;
- }
- return errors.toString();
- }
- }
-
- static class IntList {
- // not synchronized - used only in one thread
- static String render(int[] input) {
- if (null == input) {
- return "null";
- }
- StringBuffer sb = new StringBuffer();
- sb.append("[");
- for (int i = 0; i < input.length; i++) {
- if (i > 0) {
- sb.append(", " + input[i]);
- } else {
- sb.append("" + input[i]);
- }
- }
- sb.append("]");
- return sb.toString();
- }
-
- private int[] input = new int[256];
- private int insert;
- private void add(int i) {
- if (insert >= input.length) {
- int[] temp = new int[insert + 256];
- for (int j = 0; j < input.length; j++) {
- temp[j] = input[j];
- }
- input = temp;
- }
- input[insert++] = i;
- }
-
- private int[] getList() {
- int[] result = new int[insert];
- for (int i = 0; i < result.length; i++) {
- result[i] = input[i];
- }
- return result;
- }
- }
- }
|