Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

ConditionTree.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*
  2. * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.actions;
  23. import java.util.ArrayDeque;
  24. import java.util.Deque;
  25. /**
  26. * A condition tree specifies in which order a group of conditions will be
  27. * executed.
  28. *
  29. * @author chris
  30. */
  31. public class ConditionTree {
  32. /** The possible operations on a condition tree. */
  33. public static enum OPERATION {
  34. /** Only passes if both subtrees are true. */
  35. AND,
  36. /** Passes if either of the subtrees are true. */
  37. OR,
  38. /** Passes if the specified argument is true. */
  39. VAR,
  40. /** Only passes iof the left subtree fails to pass. */
  41. NOT,
  42. /** Doesn't do anything (an empty tree). */
  43. NOOP
  44. }
  45. /** The left subtree of this tree. */
  46. private ConditionTree leftArg = null;
  47. /** The right subtree of this tree. */
  48. private ConditionTree rightArg = null;
  49. /** The argument of this tree (only used for VAR ops). */
  50. private int argument = -1;
  51. /** The operation that this tree performs. */
  52. private final OPERATION op;
  53. /**
  54. * Creates a new ConditionTree for a binary operation.
  55. *
  56. * @param op The binary operation to perform
  57. * @param leftArg The left argument/subtree
  58. * @param rightArg The right argument/subtree
  59. */
  60. private ConditionTree(final OPERATION op, final ConditionTree leftArg,
  61. final ConditionTree rightArg) {
  62. this.op = op;
  63. this.leftArg = leftArg;
  64. this.rightArg = rightArg;
  65. }
  66. /**
  67. * Creates a new ConditionTree for a unary operation.
  68. *
  69. * @param op
  70. * @param argument
  71. */
  72. private ConditionTree(final OPERATION op, final ConditionTree argument) {
  73. this.op = op;
  74. this.leftArg = argument;
  75. }
  76. /**
  77. * Creates a new ConditionTree for a VAR operation with the specified
  78. * argument number.
  79. *
  80. * @param argument The number of the argument that's to be tested.
  81. */
  82. private ConditionTree(final int argument) {
  83. this.op = OPERATION.VAR;
  84. this.argument = argument;
  85. }
  86. /**
  87. * Creates a new ConditionTree for a NOOP operation.
  88. */
  89. private ConditionTree() {
  90. this.op = OPERATION.NOOP;
  91. }
  92. /**
  93. * Retrieves the highest argument number that is used in this condition tree.
  94. *
  95. * @return The highest argument number used in this tree
  96. */
  97. public int getMaximumArgument() {
  98. if (this.op == OPERATION.NOOP) {
  99. return 0;
  100. } else if (this.op == OPERATION.VAR) {
  101. return argument;
  102. } else if (this.op == OPERATION.NOT) {
  103. return leftArg.getMaximumArgument();
  104. } else {
  105. return Math.max(leftArg.getMaximumArgument(), rightArg.getMaximumArgument());
  106. }
  107. }
  108. /**
  109. * Evaluates this tree with the specified conditions. Returns the result
  110. * of the evaluation.
  111. *
  112. * @param conditions The binary values of each of the conditions used in
  113. * this three
  114. * @return The result of the evaluation of this tree
  115. */
  116. public boolean evaluate(final boolean[] conditions) {
  117. switch (op) {
  118. case VAR:
  119. return conditions[argument];
  120. case NOT:
  121. return !leftArg.evaluate(conditions);
  122. case AND:
  123. return leftArg.evaluate(conditions) && rightArg.evaluate(conditions);
  124. case OR:
  125. return leftArg.evaluate(conditions) || rightArg.evaluate(conditions);
  126. default:
  127. return true;
  128. }
  129. }
  130. /** {@inheritDoc} */
  131. @Override
  132. public boolean equals(final Object obj) {
  133. return obj instanceof ConditionTree
  134. && toString().equals(((ConditionTree) obj).toString());
  135. }
  136. /** {@inheritDoc} */
  137. @Override
  138. public int hashCode() {
  139. return toString().hashCode();
  140. }
  141. /**
  142. * Retrieves a String representation of this ConditionTree. The string
  143. * representation is a normalised formula describing this tree and all of
  144. * its children. The output of this method will generate an identical tree
  145. * if passed to parseString.
  146. *
  147. * @return A string representation of this tree
  148. */
  149. @Override
  150. public String toString() {
  151. switch (op) {
  152. case VAR:
  153. return String.valueOf(argument);
  154. case NOT:
  155. return "!" + leftArg;
  156. case AND:
  157. return "(" + leftArg + "&" + rightArg + ")";
  158. case OR:
  159. return "(" + leftArg + "|" + rightArg + ")";
  160. default:
  161. return "";
  162. }
  163. }
  164. /**
  165. * Parses the specified string into a condition tree.
  166. *
  167. * @param string The string to be parsed
  168. * @return The corresponding condition tree, or null if there was an error
  169. * while parsing the data
  170. */
  171. public static ConditionTree parseString(final String string) {
  172. final Deque<Object> stack = new ArrayDeque<Object>();
  173. for (int i = 0; i < string.length(); i++) {
  174. final char m = string.charAt(i);
  175. if (isInt(m)) {
  176. final StringBuilder temp = new StringBuilder(String.valueOf(m));
  177. while (i + 1 < string.length() && isInt(string.charAt(i + 1))) {
  178. temp.append(string.charAt(i + 1));
  179. i++;
  180. }
  181. try {
  182. stack.add(new ConditionTree(Integer.parseInt(temp.toString())));
  183. } catch (NumberFormatException ex) {
  184. return null;
  185. }
  186. } else if (m != ' ' && m != '\t' && m != '\n' && m != '\r') {
  187. stack.add(m);
  188. }
  189. }
  190. return parseStack(stack);
  191. }
  192. /**
  193. * Parses the specified stack of elements, and returns a corresponding
  194. * ConditionTree.
  195. *
  196. * @param stack The stack to be read.
  197. * @return The corresponding condition tree, or null if there was an error
  198. * while parsing the data.
  199. */
  200. private static ConditionTree parseStack(final Deque<Object> stack) {
  201. final Deque<Object> myStack = new ArrayDeque<Object>();
  202. while (!stack.isEmpty()) {
  203. final Object object = stack.poll();
  204. if (object instanceof Character && ((Character) object) == ')') {
  205. final ConditionTree bracket = readBracket(myStack);
  206. if (bracket == null) {
  207. return null;
  208. } else {
  209. myStack.add(bracket);
  210. }
  211. } else {
  212. myStack.add(object);
  213. }
  214. }
  215. while (!myStack.isEmpty()) {
  216. if (myStack.size() == 1) {
  217. final Object first = myStack.pollFirst();
  218. if (first instanceof ConditionTree) {
  219. return (ConditionTree) first;
  220. } else {
  221. return null;
  222. }
  223. }
  224. final ConditionTree first = readTerm(myStack);
  225. if (first == null) {
  226. return null;
  227. } else if (myStack.isEmpty()) {
  228. return first;
  229. }
  230. final Object second = myStack.pollFirst();
  231. if (myStack.isEmpty()) {
  232. return null;
  233. } else {
  234. final ConditionTree third = readTerm(myStack);
  235. if (third != null && second instanceof Character) {
  236. OPERATION op;
  237. if ((Character) second == '&') {
  238. op = OPERATION.AND;
  239. } else if ((Character) second == '|') {
  240. op = OPERATION.OR;
  241. } else {
  242. return null;
  243. }
  244. myStack.addFirst(new ConditionTree(op, first, third));
  245. } else {
  246. return null;
  247. }
  248. }
  249. }
  250. return new ConditionTree();
  251. }
  252. /**
  253. * Reads and returns a single term from the specified stack.
  254. *
  255. * @param stack The stack to be read
  256. * @return The ConditionTree representing the last element on the stack,
  257. * or null if it was not possible to create one.
  258. */
  259. private static ConditionTree readTerm(final Deque<Object> stack) {
  260. final Object first = stack.pollFirst();
  261. if (first instanceof Character && (Character) first == '!') {
  262. if (stack.isEmpty()) {
  263. return null;
  264. }
  265. return new ConditionTree(OPERATION.NOT, readTerm(stack));
  266. } else {
  267. if (!(first instanceof ConditionTree)) {
  268. return null;
  269. }
  270. return (ConditionTree) first;
  271. }
  272. }
  273. /**
  274. * Pops elements off of the end of the specified stack until an opening
  275. * bracket is reached, and then returns the parsed content of the bracket.
  276. *
  277. * @param stack The stack to be read for the bracket
  278. * @return The parsed contents of the bracket, or null if the brackets were
  279. * mismatched.
  280. */
  281. private static ConditionTree readBracket(final Deque<Object> stack) {
  282. final Deque<Object> tempStack = new ArrayDeque<Object>();
  283. boolean found = false;
  284. while (!found && !stack.isEmpty()) {
  285. final Object object = stack.pollLast();
  286. if (object instanceof Character && ((Character) object) == '(') {
  287. found = true;
  288. } else {
  289. tempStack.addFirst(object);
  290. }
  291. }
  292. if (found) {
  293. return parseStack(tempStack);
  294. } else {
  295. return null;
  296. }
  297. }
  298. /**
  299. * Determines if the specified character represents a single digit.
  300. *
  301. * @param target The character to be tested
  302. * @return True if the character is a digit, false otherwise
  303. */
  304. private static boolean isInt(final char target) {
  305. return target >= '0' && target <= '9';
  306. }
  307. /**
  308. * Creates a condition tree by disjoining the specified number of arguments
  309. * together.
  310. *
  311. * @param numArgs The number of arguments to be disjoined
  312. * @return The corresponding condition tree
  313. */
  314. public static ConditionTree createDisjunction(final int numArgs) {
  315. final StringBuilder builder = new StringBuilder();
  316. for (int i = 0; i < numArgs; i++) {
  317. if (builder.length() != 0) {
  318. builder.append('|');
  319. }
  320. builder.append(i);
  321. }
  322. return parseString(builder.toString());
  323. }
  324. /**
  325. * Creates a condition tree by conjoining the specified number of arguments
  326. * together.
  327. *
  328. * @param numArgs The number of arguments to be conjoined
  329. * @return The corresponding condition tree
  330. */
  331. public static ConditionTree createConjunction(final int numArgs) {
  332. final StringBuilder builder = new StringBuilder();
  333. for (int i = 0; i < numArgs; i++) {
  334. if (builder.length() != 0) {
  335. builder.append('&');
  336. }
  337. builder.append(i);
  338. }
  339. return parseString(builder.toString());
  340. }
  341. }