You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Values.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. /* *******************************************************************
  2. * Copyright (c) 2003 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Wes Isberg initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.testing.util.options;
  13. import java.util.ArrayList;
  14. import java.util.Arrays;
  15. import java.util.Collections;
  16. import java.util.Iterator;
  17. import java.util.Map;
  18. import java.util.TreeMap;
  19. import org.aspectj.testing.util.options.Option.Value;
  20. import org.aspectj.util.LangUtil;
  21. /**
  22. * Wrapper for Value[] that handles search boilerplate.
  23. */
  24. public class Values {
  25. public static final Values EMPTY;
  26. /** used by methods taking Selector to halt processing early */
  27. private static final boolean VERIFYING = true;
  28. private static final boolean FIND_ALL = true;
  29. private static final String NO_ERROR = "no error";
  30. static {
  31. EMPTY = new Values(new Value[0]);
  32. }
  33. public static Values wrapValues(Value[] values) {
  34. if ((null == values) || (0 == values.length)) {
  35. return EMPTY;
  36. }
  37. return new Values(values);
  38. }
  39. public static Values wrapValues(Values[] values) {
  40. if ((null == values) || (0 == values.length)) {
  41. return EMPTY;
  42. }
  43. Value[] input = null;
  44. if (values.length == 1) {
  45. input = values[0].asArray();
  46. LangUtil.throwIaxIfNull(input, "values");
  47. } else {
  48. int length = 0;
  49. for (int i = 0; i < values.length; i++) {
  50. if (values[i] == null) {
  51. LangUtil.throwIaxIfNull(null, "null value[" + i + "]");
  52. }
  53. length += values[i].length();
  54. }
  55. input = new Value[length];
  56. length = 0;
  57. Value[] temp;
  58. for (int i = 0; i < values.length; i++) {
  59. temp = values[i].asArray();
  60. System.arraycopy(temp, 0, input, length, temp.length);
  61. length += temp.length;
  62. }
  63. }
  64. return new Values(input);
  65. }
  66. static int[] invert(int[] missed, int length) {
  67. final int MAX = length;
  68. final int len = MAX - missed.length;
  69. final int[] result = new int[len];
  70. int missedIndex = 0;
  71. int resultIndex = 0;
  72. for (int counter = 0; counter < MAX; counter++) {
  73. // catch result up to missed
  74. while (((missedIndex >= missed.length)
  75. || (missed[missedIndex] > counter))
  76. && (counter < MAX)) {
  77. result[resultIndex++] = counter++;
  78. }
  79. // absorb missed up to counter
  80. while ((missedIndex < missed.length)
  81. && (missed[missedIndex] <= counter)
  82. && (counter < MAX)) {
  83. missedIndex++;
  84. }
  85. }
  86. return result;
  87. }
  88. private static Option.Value[] toArray(ArrayList list) {
  89. return (Option.Value[]) list.toArray(new Option.Value[0]);
  90. }
  91. /**
  92. * Resolve input to remove any values matching the same options,
  93. * where option conflicts are handled by option forcing.
  94. * First, for any force-off value, all matching set-on and
  95. * the force-off itself are removed. At this time, if there
  96. * is a matching force-on, then this will return an error.
  97. * Next, for any force-on value, it is converted to set-on,
  98. * and any other matching set-on value is removed.
  99. * Finally, this signals a collision if two values share
  100. * the same option family and the family reports that this is
  101. * a collision.
  102. * In all cases, only the first error detected is reported.
  103. * @param input the Option.Value[] matched from the input,
  104. * (forced/duplicate options will be set to null)
  105. * @return String error during resolution, or null if no error
  106. */
  107. private static String resolve(Option.Value[] input) {
  108. String err = null;
  109. if (LangUtil.isEmpty(input)) {
  110. return null;
  111. }
  112. Map familyToMatches = new TreeMap();
  113. for (int i = 0;(null == err) && (i < input.length); i++) {
  114. if (null != input[i]) {
  115. Option.Family family = input[i].option.getFamily();
  116. int[] matches = (int[]) familyToMatches.get(family);
  117. if (null == matches) {
  118. matches = match(input, i);
  119. familyToMatches.put(family, matches);
  120. }
  121. }
  122. }
  123. familyToMatches = Collections.unmodifiableMap(familyToMatches);
  124. for (Iterator iter = familyToMatches.entrySet().iterator();
  125. (null == err) && iter.hasNext();
  126. ) {
  127. Map.Entry entry = (Map.Entry) iter.next();
  128. int[] matches = (int[]) entry.getValue();
  129. err = resolve(input, matches);
  130. }
  131. return err;
  132. }
  133. /**
  134. * Resolve all related options into one
  135. * by nullifying or modifying the values.
  136. *
  137. * First, for any force-off value,
  138. * remove all identical set-on
  139. * and the force-off itself,
  140. * and alert on any identical force-on.
  141. *
  142. * Next, for any force-on value,
  143. * convert to set-on,
  144. * throw Error on any same-family force-off value,
  145. * remove any identical force-on or set-on value,
  146. * alert on any other non-identical same-family force-on value,
  147. * remove any same-family set-on value,
  148. * and alert on any same-family set-off value.
  149. *
  150. * Finally, alert if any two remaining values share
  151. * the same option family, unless the option is marked
  152. * as permitting multiple values.
  153. *
  154. * @param input the Option.Value[] matching the input
  155. * @param matches the int[] list of indexes into input for
  156. * values for related by option
  157. * (all such values must have option matched by family)
  158. * @return String error, if any, or null if no error
  159. * @see #match(Option.Value[], int)
  160. */
  161. private static String resolve(Option.Value[] input, int[] matches) {
  162. String err = null;
  163. // seek force-off
  164. // Option.Value forceOff = null;
  165. Option option = null;
  166. // find and remove any force-off
  167. for (int i = 0;(null == err) && (i < matches.length); i++) {
  168. Option.Value value = input[matches[i]];
  169. if (null != value) {
  170. // verify that matches are in the same family
  171. if (VERIFYING) {
  172. if (null == option) {
  173. option = value.option;
  174. } else if (!(option.sameOptionFamily(value.option))) {
  175. String s =
  176. value.option
  177. + " has different family from "
  178. + option;
  179. throw new IllegalArgumentException(s);
  180. }
  181. }
  182. if (value.prefix.forceOff()) {
  183. err = removeForceOff(input, value, matches);
  184. }
  185. }
  186. }
  187. // find and set any force-on, removing others
  188. for (int i = 0;(null == err) && (i < matches.length); i++) {
  189. Option.Value value = input[matches[i]];
  190. if (null != value) {
  191. if (value.prefix.forceOn()) {
  192. err = convertForceOn(input, i, matches);
  193. }
  194. }
  195. }
  196. // remove any exact duplicates
  197. for (int i = 0;(null == err) && (i < matches.length); i++) {
  198. Option.Value value = input[matches[i]];
  199. if (null != value) {
  200. for (int j = i + 1; j < matches.length; j++) {
  201. if (value.sameValueIdentifier(input[matches[j]])) {
  202. input[matches[j]] = null;
  203. }
  204. }
  205. }
  206. }
  207. // signal error if two left unless permitMultipleFamilyValues
  208. Option.Value first = null;
  209. for (int i = 0;(null == err) && (i < matches.length); i++) {
  210. Option.Value value = input[matches[i]];
  211. if (null != value) {
  212. if (null == first) {
  213. first = value;
  214. if (first
  215. .option
  216. .getFamily()
  217. .permitMultipleFamilyValues()) {
  218. break;
  219. }
  220. } else {
  221. err = "collision between " + first + " and " + value;
  222. }
  223. }
  224. }
  225. return err;
  226. }
  227. /**
  228. * For any force-off value,
  229. * remove all set-on or force-off with same value
  230. * (including the force-off itself),
  231. * and alert on any identical force-on.
  232. * @param input the Option.Value[] matching the input
  233. * @param value the force-off Option.Value to remove
  234. * @param matches the int[] list of indexes into input for
  235. * values for related by option
  236. * (all such values must have matching option)
  237. * @return String error if any
  238. */
  239. private static String removeForceOff(
  240. Option.Value[] input,
  241. Option.Value value,
  242. int[] matches) {
  243. if (!value.prefix.forceOff()) {
  244. throw new IllegalArgumentException(
  245. "expecting force-off: " + value);
  246. }
  247. for (int i = 0; i < matches.length; i++) {
  248. Option.Value match = input[matches[i]];
  249. if ((null != match) && value.sameValueIdentifier(match)) {
  250. if (match.prefix.forceOn()) {
  251. return "force conflict between "
  252. + value
  253. + " and "
  254. + match;
  255. } else {
  256. input[matches[i]] = null; // unset matches[i]?
  257. }
  258. }
  259. }
  260. return null;
  261. }
  262. /**
  263. * For this force-on value, convert to set-on,
  264. * throw Error on any same-family force-off value,
  265. * remove any identical force-on or set-on value,
  266. * alert on any other non-identical same-family force-on value,
  267. * remove any same-family set-on value,
  268. * and alert on any same-family set-off value.
  269. * This must be called after <code>removeForceOff(..)</code>.
  270. * @param input the Option.Value[] to modify
  271. * @param valueIndex the int index in matches to find the force-on
  272. * and to start after
  273. * @param matches the int[] map into input entries with matching options
  274. * @return
  275. * @throw Error if any matching force-off found
  276. */
  277. private static String convertForceOn(
  278. Option.Value[] input,
  279. int valueIndex,
  280. int[] matches) {
  281. Option.Value value = input[matches[valueIndex]];
  282. if (!value.prefix.forceOn()) {
  283. throw new IllegalArgumentException(
  284. "expecting force-on: " + value);
  285. }
  286. input[matches[valueIndex]] = value.convert(Option.ON);
  287. for (int i = 0; i < matches.length; i++) {
  288. if (i == valueIndex) {
  289. continue;
  290. }
  291. Option.Value match = input[matches[i]];
  292. if (null != match) {
  293. // assert match.sameOptionFamily(value);
  294. if (match.prefix.forceOff()) {
  295. throw new Error(
  296. "unexpected force-off:"
  297. + match
  298. + " when processing "
  299. + value);
  300. }
  301. if (value.option.sameOptionIdentifier(match.option)) {
  302. input[matches[i]] = null;
  303. // remove any identical force-on or set
  304. } else if (match.prefix.forceOn()) {
  305. return "conflict between " + match + " and " + value;
  306. } else if (match.prefix.isSet()) {
  307. input[matches[i]] = null;
  308. // remove any same-value set-on value
  309. } else { // same family, force-off
  310. return "collision between " + match + " and " + value;
  311. }
  312. }
  313. }
  314. return null;
  315. }
  316. /**
  317. * Get a list of input matching the option in the initial value,
  318. * rendered as indexes into the input array.
  319. * @param input the Option.Value[] to seek in
  320. * @param start the int index of the starting position
  321. * @return int[] of indexes into input with the same option
  322. * as index[start] - never null, but can be empty
  323. */
  324. private static int[] match(Option.Value[] input, int start) {
  325. IntList result = new IntList();
  326. Option.Family key = null;
  327. Option.Family nextKey = null;
  328. for (int i = start; i < input.length; i++) {
  329. if (null != input[i]) {
  330. nextKey = input[i].option.getFamily();
  331. if (null == key) {
  332. key = nextKey;
  333. result.add(i);
  334. } else if (key.equals(nextKey)) {
  335. result.add(i);
  336. }
  337. }
  338. }
  339. return result.getList();
  340. }
  341. static int nullify(Option.Value[] values, Selector selector) {
  342. LangUtil.throwIaxIfNull(selector, "selector");
  343. int changed = 0;
  344. for (int i = 0; i < values.length; i++) {
  345. final boolean accepted;
  346. try {
  347. accepted = selector.accept(values[i]);
  348. } catch (Error e) {
  349. if (e != Selector.STOP) {
  350. throw e;
  351. }
  352. break;
  353. }
  354. if (accepted) {
  355. if (null != values[i]) {
  356. values[i] = null;
  357. changed++;
  358. }
  359. }
  360. }
  361. return changed;
  362. }
  363. /**
  364. * Render set values as String using associated prefix.
  365. * @param values the Value[] to render
  366. * @return String[] of values rendered for output
  367. * (never null or longer than values, but might be shorter)
  368. */
  369. private static String[] render(Value[] values) {
  370. ArrayList list = new ArrayList();
  371. for (int i = 0; i < values.length; i++) {
  372. if (null != values[i]) {
  373. String[] output = values[i].unflatten();
  374. if (LangUtil.isEmpty(output)) {
  375. throw new Error("no output for " + values[i]);
  376. }
  377. String s = values[i].prefix.render(output[0]);
  378. if (null != s) { // this means the prefix is set
  379. list.add(s);
  380. for (int j = 1; j < output.length; j++) {
  381. list.add(output[j]);
  382. }
  383. }
  384. }
  385. }
  386. return (String[]) list.toArray(new String[list.size()]);
  387. }
  388. private final Option.Value[] values;
  389. private Option.Value[] valuesNotNull;
  390. private String resolveError;
  391. private Values(Value[] values) {
  392. this.values = new Value[values.length];
  393. System.arraycopy(values, 0, this.values, 0, values.length);
  394. }
  395. public int length() {
  396. return values.length;
  397. }
  398. public Option.Value[] asArray() {
  399. Option.Value[] result = new Option.Value[values.length];
  400. System.arraycopy(values, 0, result, 0, result.length);
  401. return result;
  402. }
  403. /**
  404. * Emit as String[] the non-null values.
  405. * @return String[] of matched entries (never null, elements not null)
  406. */
  407. public String[] render() {
  408. return Values.render(valuesNotNull());
  409. }
  410. public String toString() {
  411. return Arrays.asList(values).toString();
  412. }
  413. /**
  414. * Create index into values of those that were matched,
  415. * including the options and their arguments.
  416. * @return int[] of elements in values that are not null (options)
  417. * or that represent option arguments
  418. */
  419. public int[] indexMatches() {
  420. // must be in order, low to high
  421. final int[] missed = indexMissedMatches();
  422. return invert(missed, length());
  423. }
  424. /**
  425. * Create index into values of missed input,
  426. * taking into account that matched arguments are
  427. * represented as null.
  428. * @return int[] of elements in values that are null
  429. * or optionally represent option arguments
  430. */
  431. public int[] indexMissedMatches() {
  432. MissedSelector selector = new MissedSelector();
  433. find(selector, FIND_ALL);
  434. String errors = selector.getErrors();
  435. if (null != errors) {
  436. throw new Error(errors);
  437. }
  438. return selector.getResult();
  439. }
  440. public Value firstInFamily(Option.Family family) {
  441. return findFirst(new ValueSelector(family));
  442. }
  443. public Value[] allInFamily(Option.Family family) {
  444. return find(new ValueSelector(family), FIND_ALL);
  445. }
  446. public Value firstOption(Option option) {
  447. return findFirst(new ValueSelector(option));
  448. }
  449. public Value[] allOption(Option option) {
  450. return find(new ValueSelector(option), FIND_ALL);
  451. }
  452. public Value firstValue(Option option, String value) {
  453. LangUtil.throwIaxIfNull(value, "value");
  454. return findFirst(new ValueSelector(option, value));
  455. }
  456. public Value[] allValues(Option option, String value) {
  457. LangUtil.throwIaxIfNull(value, "value");
  458. return find(new ValueSelector(option, value), FIND_ALL);
  459. }
  460. public boolean isResolved() {
  461. return ((this != EMPTY) && (null != resolveError));
  462. }
  463. /**
  464. *
  465. * @param selector the Selector to pick out entries to nullify
  466. * (should throw STOP to halt processing)
  467. * @return Values resulting from nullifying entries,
  468. * or this if none were changed
  469. */
  470. public Values nullify(Selector selector) {
  471. if (null == selector) {
  472. return this;
  473. }
  474. Value[] temp = asArray();
  475. int changed = nullify(temp, selector);
  476. if (0 == changed) {
  477. return this;
  478. }
  479. return new Values(temp);
  480. }
  481. /**
  482. * Resolve options, removing duplicates by force if necessary.
  483. * If any error is returned, then the values are left unchanged.
  484. * @return String error, if any
  485. * @throws IllegalStateException if <code>isResolved()</code>
  486. */
  487. public String resolve() {
  488. if (isResolved()) {
  489. throw new IllegalStateException("already resolved");
  490. }
  491. Option.Value[] temp = asArray();
  492. resolveError = resolve(temp);
  493. if (null == resolveError) {
  494. System.arraycopy(temp, 0, values, 0, temp.length);
  495. valuesNotNull = null;
  496. resolveError = NO_ERROR;
  497. return null;
  498. }
  499. return resolveError;
  500. }
  501. protected Option.Value findFirst(Selector filter) {
  502. Option.Value[] result = find(filter, !FIND_ALL);
  503. return (0 == result.length ? null : result[0]);
  504. }
  505. protected Option.Value[] find(Selector filter, boolean findAll) {
  506. LangUtil.throwIaxIfNull(filter, "filter");
  507. ArrayList result = new ArrayList();
  508. for (int i = 0; i < values.length; i++) {
  509. final boolean accepted;
  510. try {
  511. accepted = filter.accept(values[i]);
  512. } catch (Error e) {
  513. if (Selector.STOP != e) {
  514. throw e;
  515. }
  516. break;
  517. }
  518. if (accepted) {
  519. result.add(values[i]);
  520. if (findAll != FIND_ALL) {
  521. break;
  522. }
  523. }
  524. }
  525. return toArray(result);
  526. }
  527. private Option.Value[] valuesNotNull() {
  528. if (null == valuesNotNull) {
  529. ArrayList list = new ArrayList();
  530. for (int i = 0; i < this.values.length; i++) {
  531. if (null != this.values[i]) {
  532. list.add(this.values[i]);
  533. }
  534. }
  535. valuesNotNull = toArray(list);
  536. }
  537. return valuesNotNull;
  538. }
  539. public static class Selector {
  540. public static final Error STOP = new Error("stop invoking Selector");
  541. protected Selector() {
  542. }
  543. protected boolean accept(Value value) {
  544. return false;
  545. }
  546. }
  547. protected static class ValueSelector extends Selector {
  548. private final Option option;
  549. private final Option.Family family;
  550. private final String value;
  551. ValueSelector(Option.Family family) {
  552. LangUtil.throwIaxIfNull(family, "family");
  553. this.family = family;
  554. option = null;
  555. value = null;
  556. }
  557. ValueSelector(Option option) {
  558. this(option, (String) null);
  559. }
  560. ValueSelector(Option option, String value) {
  561. LangUtil.throwIaxIfNull(option, "option");
  562. this.option = option;
  563. family = null;
  564. this.value = value;
  565. }
  566. protected boolean accept(Value value) {
  567. if (null == value) {
  568. return false;
  569. }
  570. if (null != family) {
  571. return family.sameFamily(value.option.getFamily());
  572. } else if (!option.sameOptionIdentifier(value.option)) {
  573. return false;
  574. } else {
  575. return ((null == this.value)
  576. || (this.value.equals(value.value)));
  577. }
  578. }
  579. }
  580. /** pick all null entries (except for args), return as int[] */
  581. protected static class MissedSelector extends Selector {
  582. public static final String DELIM = "; ";
  583. final IntList result = new IntList();
  584. int index;
  585. final StringBuffer errors = new StringBuffer();
  586. int argsExpected;
  587. Option argsExpectedFor;
  588. MissedSelector() {
  589. }
  590. int[] getResult() {
  591. return result.getList();
  592. }
  593. /**
  594. * add index if value is null
  595. * unless skipArguments
  596. */
  597. protected boolean accept(Value value) {
  598. index++;
  599. if (null != value) {
  600. if (0 < argsExpected) { // expected more (null) args
  601. missedArgsFor(argsExpectedFor, argsExpected);
  602. }
  603. argsExpected = value.option.numArguments();
  604. argsExpectedFor = value.option;
  605. } else if (0 < argsExpected) { // ignore null in arg position
  606. argsExpected--;
  607. if (0 == argsExpected) {
  608. argsExpectedFor = null;
  609. }
  610. } else { // null, not expecting arg, so missing
  611. result.add(index - 1);
  612. return true;
  613. }
  614. return false;
  615. }
  616. private void missedArgsFor(Option option, int numArgs) {
  617. errors.append("missed ");
  618. errors.append(numArgs + " args for ");
  619. errors.append(option + DELIM);
  620. }
  621. String getErrors() {
  622. if (0 < argsExpected) {
  623. }
  624. if (0 == errors.length()) {
  625. return null;
  626. }
  627. return errors.toString();
  628. }
  629. }
  630. static class IntList {
  631. // not synchronized - used only in one thread
  632. static String render(int[] input) {
  633. if (null == input) {
  634. return "null";
  635. }
  636. StringBuffer sb = new StringBuffer();
  637. sb.append("[");
  638. for (int i = 0; i < input.length; i++) {
  639. if (i > 0) {
  640. sb.append(", " + input[i]);
  641. } else {
  642. sb.append("" + input[i]);
  643. }
  644. }
  645. sb.append("]");
  646. return sb.toString();
  647. }
  648. private int[] input = new int[256];
  649. private int insert;
  650. private void add(int i) {
  651. if (insert >= input.length) {
  652. int[] temp = new int[insert + 256];
  653. for (int j = 0; j < input.length; j++) {
  654. temp[j] = input[j];
  655. }
  656. input = temp;
  657. }
  658. input[insert++] = i;
  659. }
  660. private int[] getList() {
  661. int[] result = new int[insert];
  662. for (int i = 0; i < result.length; i++) {
  663. result[i] = input[i];
  664. }
  665. return result;
  666. }
  667. }
  668. }