PageRenderTime 25ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/web/action/util/workflow/WorkflowEditorTransitionConditionUtil.java

https://bitbucket.org/ahmed_bilal_360factors/jira7-core
Java | 311 lines | 213 code | 53 blank | 45 comment | 65 complexity | c9706e2e0b9b16fb0be0ce1efbb909b2 MD5 | raw file
Possible License(s): Apache-2.0
  1. package com.atlassian.jira.web.action.util.workflow;
  2. import com.opensymphony.util.TextUtils;
  3. import com.opensymphony.workflow.loader.ActionDescriptor;
  4. import com.opensymphony.workflow.loader.ConditionDescriptor;
  5. import com.opensymphony.workflow.loader.ConditionsDescriptor;
  6. import com.opensymphony.workflow.loader.DescriptorFactory;
  7. import com.opensymphony.workflow.loader.RestrictionDescriptor;
  8. import org.apache.commons.lang.StringUtils;
  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.List;
  12. public class WorkflowEditorTransitionConditionUtil {
  13. /**
  14. * Separator for the 'count' variables that is used to represent a position in a hierarchy.
  15. */
  16. public static final String SEPARATOR = ".";
  17. public static final String OPERATOR_AND = "AND";
  18. public static final String OPERATOR_OR = "OR";
  19. /**
  20. * Returns a {@link ConditionsDescriptor} in the given {@link RestrictionDescriptor} in the 'place' specified by the
  21. * count. The count is a {@link #SEPARATOR} separated integers that represents the place of the ConditionsDescriptor
  22. * in the hierarchy of nested ConditionsDescriptors.
  23. * <p>
  24. * This method assumes that the last integer in count will be used to find a {@link com.opensymphony.workflow.loader.ConditionDescriptor} in the
  25. * returned {@link ConditionsDescriptor}, so is not used while looking through the hierarchy.
  26. * </p>
  27. * <p>
  28. * For example, if this method is called with count "1.2.4", this method will take the root ConditionsDescriptor of the passed
  29. * RestrictionDescriptor, look up a nested ConditionsDescriptor at index 0. Them another lookup will be done for another nested
  30. * ConditionsDescriptor at index 1. This ConditionsDescriptor will be returned, as "4" is ignored.
  31. * </p>
  32. *
  33. * @throws IllegalArgumentException if the object at any index specified by the count is not an instance of {@link ConditionsDescriptor}
  34. */
  35. public ConditionsDescriptor getParentConditionsDescriptor(RestrictionDescriptor restrictionDescriptor, String count) {
  36. return findConfitionsDescriptor(restrictionDescriptor, count, 1);
  37. }
  38. /**
  39. * Same as {@link #getParentConditionsDescriptor(com.opensymphony.workflow.loader.RestrictionDescriptor, String)} only all the
  40. * indexes specified by the passed in count are used for lookups. That is, the last integer in count is <b>NOT</b> ignored.
  41. */
  42. public ConditionsDescriptor getConditionsDescriptor(RestrictionDescriptor restrictionDescriptor, String count) {
  43. return findConfitionsDescriptor(restrictionDescriptor, count, 0);
  44. }
  45. private ConditionsDescriptor findConfitionsDescriptor(RestrictionDescriptor restrictionDescriptor, String count, int offset) {
  46. ConditionsDescriptor conditionsDescriptor = restrictionDescriptor.getConditionsDescriptor();
  47. String[] counts = StringUtils.split(count, SEPARATOR);
  48. if (counts != null && counts.length > offset) {
  49. for (int i = 0; i < counts.length - offset; i++) {
  50. int c = Integer.parseInt(counts[i]);
  51. List<?> conditions = conditionsDescriptor.getConditions();
  52. if (conditions.get(c - 1) instanceof ConditionsDescriptor) {
  53. conditionsDescriptor = (ConditionsDescriptor) conditions.get(c - 1);
  54. } else {
  55. throw new IllegalArgumentException("The descriptor at count " + count + " is not a ConditionsDescriptor.");
  56. }
  57. }
  58. }
  59. return conditionsDescriptor;
  60. }
  61. public void getGrandParentConditionsDescriptor(RestrictionDescriptor restriction, String count) {
  62. }
  63. /**
  64. * Checks if restrictionDescriptor has no {@link com.opensymphony.workflow.loader.ConditionDescriptor}s or
  65. * {@link ConditionsDescriptor}s left.
  66. */
  67. public boolean isEmpty(RestrictionDescriptor restrictionDescriptor) {
  68. ConditionsDescriptor conditionsDescriptor = restrictionDescriptor.getConditionsDescriptor();
  69. if (conditionsDescriptor == null)
  70. return true;
  71. Collection<?> conditions = conditionsDescriptor.getConditions();
  72. return (conditions == null) || (conditions.isEmpty());
  73. }
  74. /**
  75. * Checks if restrictionDescriptor has no {@link ConditionsDescriptor}s left.
  76. */
  77. public boolean isEmpty(ConditionsDescriptor conditionsDescriptor) {
  78. Collection<?> conditions = conditionsDescriptor.getConditions();
  79. return (conditions == null) || (conditions.isEmpty());
  80. }
  81. public String increaseCountLevel(String c) {
  82. if (c == null)
  83. return null;
  84. int index = c.lastIndexOf(SEPARATOR);
  85. if (index < 0)
  86. throw new IllegalArgumentException("The string '" + c + "' does not contain '" + SEPARATOR + "'.");
  87. else
  88. return c.substring(0, index);
  89. }
  90. public int getLastCount(String count) {
  91. int index = count.lastIndexOf(SEPARATOR);
  92. if (index < 0)
  93. return Integer.parseInt(count);
  94. else if (index + 1 >= count.length())
  95. throw new IllegalArgumentException("Cannot find last index in '" + count + "'.");
  96. else
  97. return Integer.parseInt(count.substring(index + 1));
  98. }
  99. public String addCondition(ActionDescriptor transition, String count, ConditionDescriptor condition) {
  100. RestrictionDescriptor restriction = getOrCreateRestriction(transition);
  101. ConditionsDescriptor conditionsDescriptor = restriction.getConditionsDescriptor();
  102. if (conditionsDescriptor == null) {
  103. // A first condition is being added
  104. conditionsDescriptor = DescriptorFactory.getFactory().createConditionsDescriptor();
  105. restriction.setConditionsDescriptor(conditionsDescriptor);
  106. conditionsDescriptor.setType(OPERATOR_AND); //this happens by default anyhow I believe, but stating explicitly
  107. conditionsDescriptor.getConditions().add(condition);
  108. return "1";
  109. } else {
  110. conditionsDescriptor = getParentConditionsDescriptor(restriction, count);
  111. if (conditionsDescriptor.getConditions() == null) {
  112. conditionsDescriptor.setConditions(new ArrayList());
  113. }
  114. // Add the condition ot the end of the list
  115. conditionsDescriptor.getConditions().add(condition);
  116. if (!TextUtils.stringSet(conditionsDescriptor.getType())) {
  117. conditionsDescriptor.setType(OPERATOR_AND);
  118. }
  119. if (isRootCount(count)) {
  120. return "" + conditionsDescriptor.getConditions().size();
  121. } else {
  122. return increaseCountLevel(count) + SEPARATOR + conditionsDescriptor.getConditions().size();
  123. }
  124. }
  125. }
  126. public String addNestedCondition(ActionDescriptor transition, String count, ConditionDescriptor condition) {
  127. RestrictionDescriptor restriction = getOrCreateRestriction(transition);
  128. ConditionsDescriptor conditionsDescriptor = restriction.getConditionsDescriptor();
  129. if (conditionsDescriptor == null) {
  130. // A first condition is being added
  131. conditionsDescriptor = DescriptorFactory.getFactory().createConditionsDescriptor();
  132. restriction.setConditionsDescriptor(conditionsDescriptor);
  133. conditionsDescriptor.setType(OPERATOR_AND); //this happens by default anyhow I believe, but stating explicitly
  134. conditionsDescriptor.getConditions().add(condition);
  135. return "1";
  136. } else {
  137. conditionsDescriptor = getParentConditionsDescriptor(restriction, count);
  138. int index = getLastCount(count) - 1;
  139. // Remove the existing condition as we will put a nested ConditionsDescriptor in its place.
  140. // The nested ConditionsDescriptor will have this condition and the new one in it.
  141. Object d = conditionsDescriptor.getConditions().remove(index);
  142. // This should always be a condition descriptor
  143. if (d instanceof ConditionDescriptor) {
  144. ConditionDescriptor existingCondition = (ConditionDescriptor) d;
  145. ConditionsDescriptor nestedDescriptor = DescriptorFactory.getFactory().createConditionsDescriptor();
  146. nestedDescriptor.setType(OPERATOR_AND);
  147. // Ensure that the conditions list is initialised for the ConditionsDescriptor
  148. if (nestedDescriptor.getConditions() == null) {
  149. nestedDescriptor.setConditions(new ArrayList());
  150. }
  151. // Add the existing condition and the new one to the nested ConditionsDescriptor
  152. nestedDescriptor.getConditions().add(existingCondition);
  153. nestedDescriptor.getConditions().add(condition);
  154. // Find the place where we can insert the nested block. All the ConditionsDescriptors must
  155. // appear before ConditionDescriptors as mandated by the OSWorkflow DTD
  156. int insertIndex = findNestedBlockInsertIndex(conditionsDescriptor);
  157. // Add the nested descriptor to where the existing condition used to be
  158. conditionsDescriptor.getConditions().add(insertIndex, nestedDescriptor);
  159. StringBuilder returnIndex = new StringBuilder();
  160. if (!isRootCount(count)) {
  161. returnIndex.append(increaseCountLevel(count));
  162. returnIndex.append(SEPARATOR);
  163. }
  164. returnIndex.append(insertIndex + 1);
  165. returnIndex.append(SEPARATOR);
  166. returnIndex.append(nestedDescriptor.getConditions().size());
  167. return returnIndex.toString();
  168. } else {
  169. throw new IllegalArgumentException("Descriptor at position '" + count + "' must be a ConditionDescriptor");
  170. }
  171. }
  172. }
  173. private RestrictionDescriptor getOrCreateRestriction(ActionDescriptor transition) {
  174. RestrictionDescriptor restriction = transition.getRestriction();
  175. if (restriction == null) {
  176. restriction = new RestrictionDescriptor();
  177. transition.setRestriction(restriction);
  178. }
  179. return restriction;
  180. }
  181. private int findNestedBlockInsertIndex(ConditionsDescriptor conditionsDescriptor) {
  182. Collection<?> conditions = conditionsDescriptor.getConditions();
  183. if (conditions == null)
  184. return 0;
  185. int i = 0;
  186. for (Object descriptor : conditions) {
  187. if (descriptor instanceof ConditionDescriptor) {
  188. return i;
  189. } else if (descriptor instanceof ConditionsDescriptor) {
  190. i++;
  191. } else {
  192. throw new IllegalArgumentException("Cannot process class '" + descriptor.getClass().getName() + "'.");
  193. }
  194. }
  195. return i;
  196. }
  197. public void deleteCondition(ActionDescriptor transition, String count) {
  198. RestrictionDescriptor restriction = transition.getRestriction();
  199. if (restriction != null) {
  200. ConditionsDescriptor conditionsDescriptor = getParentConditionsDescriptor(restriction, count);
  201. conditionsDescriptor.getConditions().remove(getLastCount(count) - 1);
  202. if (isEmpty(conditionsDescriptor)) {
  203. while (isEmpty(conditionsDescriptor)) {
  204. if (isRootCount(count)) {
  205. transition.setRestriction(null);
  206. break;
  207. } else {
  208. count = increaseCountLevel(count);
  209. conditionsDescriptor = getParentConditionsDescriptor(restriction, count);
  210. conditionsDescriptor.getConditions().remove(getLastCount(count) - 1);
  211. }
  212. }
  213. } else if (conditionsDescriptor.getConditions().size() == 1) {
  214. Object descriptor = conditionsDescriptor.getConditions().get(0);
  215. if (descriptor instanceof ConditionDescriptor) {
  216. // Only fallten out the structure if we are not dealing with the Root Conditions block
  217. if (!isRootCount(count)) {
  218. ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
  219. count = increaseCountLevel(count);
  220. ConditionsDescriptor conditionsDescriptor2 = getParentConditionsDescriptor(restriction, count);
  221. int index = getLastCount(count) - 1;
  222. conditionsDescriptor2.getConditions().remove(index);
  223. // As we are inserting a ConditionDescriptor we need to find a place for it after all the
  224. // ConditionsDescriptos in conditionsDescriptor2. Remember that the OSWorkflow DTD forces us
  225. // To have ConditionsDescriptors before ConditionDescriptos
  226. index = findNestedBlockInsertIndex(conditionsDescriptor2);
  227. conditionsDescriptor2.getConditions().add(index, conditionDescriptor);
  228. }
  229. } else if (descriptor instanceof ConditionsDescriptor) {
  230. ConditionsDescriptor cd = (ConditionsDescriptor) descriptor;
  231. conditionsDescriptor.getConditions().remove(0);
  232. if (cd.getConditions() != null && !cd.getConditions().isEmpty()) {
  233. conditionsDescriptor.getConditions().addAll(cd.getConditions());
  234. }
  235. } else {
  236. throw new IllegalArgumentException("Cannot deal with '" + descriptor.getClass().getName() + "'.");
  237. }
  238. }
  239. }
  240. }
  241. private boolean isRootCount(String count) {
  242. if (TextUtils.stringSet(count))
  243. return count.indexOf(SEPARATOR) < 0;
  244. else
  245. return true;
  246. }
  247. public void changeLogicOperator(ActionDescriptor transition, String count) {
  248. RestrictionDescriptor restriction = transition.getRestriction();
  249. if (restriction != null) {
  250. ConditionsDescriptor conditionsDescriptor = getParentConditionsDescriptor(restriction, count);
  251. if (OPERATOR_AND.equals(conditionsDescriptor.getType())) {
  252. conditionsDescriptor.setType(OPERATOR_OR);
  253. } else {
  254. conditionsDescriptor.setType(OPERATOR_AND);
  255. }
  256. }
  257. }
  258. }