Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

ConditionTree.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. * Copyright (c) 2006-2015 DMDirc Developers
  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 executed.
  27. */
  28. public final class ConditionTree {
  29. /** The possible operations on a condition tree. */
  30. public enum OPERATION {
  31. /** Only passes if both subtrees are true. */
  32. AND,
  33. /** Passes if either of the subtrees are true. */
  34. OR,
  35. /** Passes if the specified argument is true. */
  36. VAR,
  37. /** Only passes iof the left subtree fails to pass. */
  38. NOT,
  39. /** Doesn't do anything (an empty tree). */
  40. NOOP
  41. }
  42. /** The left subtree of this tree. */
  43. private ConditionTree leftArg;
  44. /** The right subtree of this tree. */
  45. private ConditionTree rightArg;
  46. /** The argument of this tree (only used for VAR ops). */
  47. private int argument = -1;
  48. /** The operation that this tree performs. */
  49. private final OPERATION op;
  50. /**
  51. * Creates a new ConditionTree for a binary operation.
  52. *
  53. * @param op The binary operation to perform
  54. * @param leftArg The left argument/subtree
  55. * @param rightArg The right argument/subtree
  56. */
  57. private ConditionTree(final OPERATION op, final ConditionTree leftArg,
  58. final ConditionTree rightArg) {
  59. this.op = op;
  60. this.leftArg = leftArg;
  61. this.rightArg = rightArg;
  62. }
  63. /**
  64. * Creates a new ConditionTree for a unary operation.
  65. *
  66. * @param op The unary operation to perform.
  67. * @param argument The argument/subtree to perform it on.
  68. */
  69. private ConditionTree(final OPERATION op, final ConditionTree argument) {
  70. this.op = op;
  71. this.leftArg = argument;
  72. }
  73. /**
  74. * Creates a new ConditionTree for a VAR operation with the specified argument number.
  75. *
  76. * @param argument The number of the argument that's to be tested.
  77. */
  78. private ConditionTree(final int argument) {
  79. this.op = OPERATION.VAR;
  80. this.argument = argument;
  81. }
  82. /**
  83. * Creates a new ConditionTree for a NOOP operation.
  84. */
  85. private ConditionTree() {
  86. this.op = OPERATION.NOOP;
  87. }
  88. /**
  89. * Retrieves the highest argument number that is used in this condition tree.
  90. *
  91. * @return The highest argument number used in this tree
  92. */
  93. public int getMaximumArgument() {
  94. if (this.op == OPERATION.NOOP) {
  95. return 0;
  96. } else if (this.op == OPERATION.VAR) {
  97. return argument;
  98. } else if (this.op == OPERATION.NOT) {
  99. return leftArg.getMaximumArgument();
  100. } else {
  101. return Math.max(leftArg.getMaximumArgument(), rightArg.getMaximumArgument());
  102. }
  103. }
  104. /**
  105. * Evaluates this tree with the specified conditions. Returns the result of the evaluation.
  106. *
  107. * @param conditions The binary values of each of the conditions used in this three
  108. *
  109. * @return The result of the evaluation of this tree
  110. */
  111. public boolean evaluate(final boolean[] conditions) {
  112. switch (op) {
  113. case VAR:
  114. return conditions[argument];
  115. case NOT:
  116. return !leftArg.evaluate(conditions);
  117. case AND:
  118. return leftArg.evaluate(conditions) && rightArg.evaluate(conditions);
  119. case OR:
  120. return leftArg.evaluate(conditions) || rightArg.evaluate(conditions);
  121. default:
  122. return true;
  123. }
  124. }
  125. @Override
  126. public boolean equals(final Object obj) {
  127. return obj instanceof ConditionTree
  128. && toString().equals(obj.toString());
  129. }
  130. @Override
  131. public int hashCode() {
  132. return toString().hashCode();
  133. }
  134. /**
  135. * Retrieves a String representation of this ConditionTree. The string representation is a
  136. * normalised formula describing this tree and all of its children. The output of this method
  137. * will generate an identical tree if passed to parseString.
  138. *
  139. * @return A string representation of this tree
  140. */
  141. @Override
  142. public String toString() {
  143. switch (op) {
  144. case VAR:
  145. return String.valueOf(argument);
  146. case NOT:
  147. return "!" + leftArg;
  148. case AND:
  149. return "(" + leftArg + "&" + rightArg + ")";
  150. case OR:
  151. return "(" + leftArg + "|" + rightArg + ")";
  152. default:
  153. return "";
  154. }
  155. }
  156. /**
  157. * Parses the specified string into a condition tree.
  158. *
  159. * @param string The string to be parsed
  160. *
  161. * @return The corresponding condition tree, or null if there was an error while parsing the
  162. * data
  163. */
  164. public static ConditionTree parseString(final String string) {
  165. final Deque<Object> stack = new ArrayDeque<>();
  166. for (int i = 0; i < string.length(); i++) {
  167. final char m = string.charAt(i);
  168. if (isInt(m)) {
  169. final StringBuilder temp = new StringBuilder(String.valueOf(m));
  170. while (i + 1 < string.length() && isInt(string.charAt(i + 1))) {
  171. temp.append(string.charAt(i + 1));
  172. i++;
  173. }
  174. try {
  175. stack.add(new ConditionTree(Integer.parseInt(temp.toString())));
  176. } catch (NumberFormatException ex) {
  177. return null;
  178. }
  179. } else if (m != ' ' && m != '\t' && m != '\n' && m != '\r') {
  180. stack.add(m);
  181. }
  182. }
  183. return parseStack(stack);
  184. }
  185. /**
  186. * Parses the specified stack of elements, and returns a corresponding ConditionTree.
  187. *
  188. * @param stack The stack to be read.
  189. *
  190. * @return The corresponding condition tree, or null if there was an error while parsing the
  191. * data.
  192. */
  193. private static ConditionTree parseStack(final Deque<Object> stack) {
  194. final Deque<Object> myStack = new ArrayDeque<>();
  195. while (!stack.isEmpty()) {
  196. final Object object = stack.poll();
  197. if (object instanceof Character && ((Character) object) == ')') {
  198. final ConditionTree bracket = readBracket(myStack);
  199. if (bracket == null) {
  200. return null;
  201. } else {
  202. myStack.add(bracket);
  203. }
  204. } else {
  205. myStack.add(object);
  206. }
  207. }
  208. while (!myStack.isEmpty()) {
  209. if (myStack.size() == 1) {
  210. final Object first = myStack.pollFirst();
  211. if (first instanceof ConditionTree) {
  212. return (ConditionTree) first;
  213. } else {
  214. return null;
  215. }
  216. }
  217. final ConditionTree first = readTerm(myStack);
  218. if (first == null) {
  219. return null;
  220. } else if (myStack.isEmpty()) {
  221. return first;
  222. }
  223. final Object second = myStack.pollFirst();
  224. if (myStack.isEmpty()) {
  225. return null;
  226. } else {
  227. final ConditionTree third = readTerm(myStack);
  228. if (third != null && second instanceof Character) {
  229. final OPERATION op;
  230. if ((Character) second == '&') {
  231. op = OPERATION.AND;
  232. } else if ((Character) second == '|') {
  233. op = OPERATION.OR;
  234. } else {
  235. return null;
  236. }
  237. myStack.addFirst(new ConditionTree(op, first, third));
  238. } else {
  239. return null;
  240. }
  241. }
  242. }
  243. return new ConditionTree();
  244. }
  245. /**
  246. * Reads and returns a single term from the specified stack.
  247. *
  248. * @param stack The stack to be read
  249. *
  250. * @return The ConditionTree representing the last element on the stack, or null if it was not
  251. * possible to create one.
  252. */
  253. private static ConditionTree readTerm(final Deque<Object> stack) {
  254. final Object first = stack.pollFirst();
  255. if (first instanceof Character && (Character) first == '!') {
  256. if (stack.isEmpty()) {
  257. return null;
  258. }
  259. return new ConditionTree(OPERATION.NOT, readTerm(stack));
  260. } else {
  261. if (!(first instanceof ConditionTree)) {
  262. return null;
  263. }
  264. return (ConditionTree) first;
  265. }
  266. }
  267. /**
  268. * Pops elements off of the end of the specified stack until an opening bracket is reached, and
  269. * then returns the parsed content of the bracket.
  270. *
  271. * @param stack The stack to be read for the bracket
  272. *
  273. * @return The parsed contents of the bracket, or null if the brackets were mismatched.
  274. */
  275. private static ConditionTree readBracket(final Deque<Object> stack) {
  276. final Deque<Object> tempStack = new ArrayDeque<>();
  277. boolean found = false;
  278. while (!found && !stack.isEmpty()) {
  279. final Object object = stack.pollLast();
  280. if (object instanceof Character && ((Character) object) == '(') {
  281. found = true;
  282. } else {
  283. tempStack.addFirst(object);
  284. }
  285. }
  286. if (found) {
  287. return parseStack(tempStack);
  288. } else {
  289. return null;
  290. }
  291. }
  292. /**
  293. * Determines if the specified character represents a single digit.
  294. *
  295. * @param target The character to be tested
  296. *
  297. * @return True if the character is a digit, false otherwise
  298. */
  299. private static boolean isInt(final char target) {
  300. return target >= '0' && target <= '9';
  301. }
  302. /**
  303. * Creates a condition tree by disjointing the specified number of arguments together.
  304. *
  305. * @param numArgs The number of arguments to be disjointed
  306. *
  307. * @return The corresponding condition tree
  308. */
  309. public static ConditionTree createDisjunction(final int numArgs) {
  310. final StringBuilder builder = new StringBuilder();
  311. for (int i = 0; i < numArgs; i++) {
  312. if (builder.length() != 0) {
  313. builder.append('|');
  314. }
  315. builder.append(i);
  316. }
  317. return parseString(builder.toString());
  318. }
  319. /**
  320. * Creates a condition tree by conjoining the specified number of arguments together.
  321. *
  322. * @param numArgs The number of arguments to be conjoined
  323. *
  324. * @return The corresponding condition tree
  325. */
  326. public static ConditionTree createConjunction(final int numArgs) {
  327. final StringBuilder builder = new StringBuilder();
  328. for (int i = 0; i < numArgs; i++) {
  329. if (builder.length() != 0) {
  330. builder.append('&');
  331. }
  332. builder.append(i);
  333. }
  334. return parseString(builder.toString());
  335. }
  336. }