/testability-explorer/src/main/java/com/google/test/metric/Cost.java

http://testability-explorer.googlecode.com/ · Java · 229 lines · 179 code · 30 blank · 20 comment · 26 complexity · ecb4387d5c16c1af862216475de08c82 MD5 · raw file

  1. /*
  2. * Copyright 2007 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.google.test.metric;
  17. import java.util.Arrays;
  18. import java.util.HashMap;
  19. import java.util.Map;
  20. public class Cost {
  21. private static final int[] EMPTY = new int[0];
  22. private static final String COMPLEXITY_COST_HELP_URL = "http://code.google.com/p/testability-explorer/wiki/ComplexityCostExplanation";
  23. private static final String GLOBAL_COST_HELP_URL = "http://code.google.com/p/testability-explorer/wiki/GlobalCostExplanation";
  24. private static final String LAW_OF_DEMETER_COST_HELP_URL = "http://code.google.com/p/testability-explorer/wiki/LawOfDemeterCostExplanation";
  25. private int cyclomaticCost;
  26. private int globalCost;
  27. private int[] lodDistribution;
  28. public Cost() {
  29. this(0, 0, EMPTY);
  30. }
  31. public Cost(int cyclomaticCost, int globalCost, int[] lodDistribution) {
  32. this.cyclomaticCost = cyclomaticCost;
  33. this.globalCost = globalCost;
  34. this.lodDistribution = lodDistribution;
  35. }
  36. public static Cost global(int count) {
  37. return new Cost(0, count, EMPTY);
  38. }
  39. public static Cost lod(int distance) {
  40. int[] distribution = new int[distance + 1];
  41. distribution[distance] = 1;
  42. return new Cost(0, 0, distribution);
  43. }
  44. public static Cost cyclomatic(int cyclomaticCost) {
  45. return new Cost(cyclomaticCost, 0, EMPTY);
  46. }
  47. public static Object lodDistribution(int... counts) {
  48. return new Cost(0, 0, counts);
  49. }
  50. public Cost add(Cost cost) {
  51. cyclomaticCost += cost.cyclomaticCost;
  52. globalCost += cost.globalCost;
  53. int[] other = cost.lodDistribution;
  54. int size = Math.max(lodDistribution.length, other.length);
  55. int[] old = lodDistribution;
  56. if (lodDistribution.length < size) {
  57. lodDistribution = new int[size];
  58. }
  59. for (int i = 0; i < size; i++) {
  60. int count1 = i < old.length ? old[i] : 0;
  61. int count2 = i < other.length ? other[i] : 0;
  62. lodDistribution[i] = count1 + count2;
  63. }
  64. return this;
  65. }
  66. public void addWithoutLod(Cost cost) {
  67. cyclomaticCost += cost.cyclomaticCost;
  68. globalCost += cost.globalCost;
  69. }
  70. public int getCyclomaticComplexityCost() {
  71. return cyclomaticCost;
  72. }
  73. public int getGlobalCost() {
  74. return globalCost;
  75. }
  76. @Override
  77. public String toString() {
  78. StringBuilder builder = new StringBuilder();
  79. String sep = "";
  80. if (cyclomaticCost > 0) {
  81. builder.append(sep);
  82. builder.append("CC: " + cyclomaticCost);
  83. sep = ", ";
  84. }
  85. if (globalCost > 0) {
  86. builder.append(sep);
  87. builder.append("GC: " + globalCost);
  88. sep = ", ";
  89. }
  90. int loDSum = getLoDSum();
  91. if (loDSum > 0) {
  92. builder.append(sep);
  93. builder.append("LOD: " + loDSum);
  94. sep = ", ";
  95. }
  96. return builder.toString();
  97. }
  98. // TODO(jwolter): Refactor me so the html presentation representation is outside the Cost class.
  99. // Probably this means configure the urls in the template, and take all the cost types directly
  100. // in there. Build this string in the templating side of things, rather than in the core Cost
  101. // side of things here. But more important first is to write up useful html pages explaining the
  102. // costs.
  103. public String toHtmlReportString() {
  104. StringBuilder builder = new StringBuilder();
  105. String sep = "";
  106. if (cyclomaticCost > 0) {
  107. builder.append(sep);
  108. builder.append("<a href=\"" + COMPLEXITY_COST_HELP_URL + "\">CC: " + cyclomaticCost + "</a>");
  109. sep = ", ";
  110. }
  111. if (globalCost > 0) {
  112. builder.append(sep);
  113. builder.append("<a href=\"" + GLOBAL_COST_HELP_URL + "\">GC: " + globalCost + "</a>");
  114. sep = ", ";
  115. }
  116. int loDSum = getLoDSum();
  117. if (loDSum > 0) {
  118. builder.append(sep);
  119. builder.append("<a href=\"" + LAW_OF_DEMETER_COST_HELP_URL + "\">LOD: " + loDSum + "</a>");
  120. sep = ", ";
  121. }
  122. return builder.toString();
  123. }
  124. public int getLoDSum() {
  125. int sum = 0;
  126. for (int value : lodDistribution) {
  127. sum += value;
  128. }
  129. return sum;
  130. }
  131. public Cost copy() {
  132. return new Cost(cyclomaticCost, globalCost, lodDistribution);
  133. }
  134. public Cost copyNoLOD() {
  135. return new Cost(cyclomaticCost, globalCost, EMPTY);
  136. }
  137. public int[] getLoDDistribution() {
  138. return lodDistribution;
  139. }
  140. @Override
  141. public int hashCode() {
  142. final int prime = 31;
  143. int result = 1;
  144. result = prime * result + cyclomaticCost;
  145. result = prime * result + globalCost;
  146. result = prime * result + Arrays.hashCode(lodDistribution);
  147. return result;
  148. }
  149. @Override
  150. public boolean equals(Object obj) {
  151. if (this == obj) {
  152. return true;
  153. }
  154. if (obj == null) {
  155. return false;
  156. }
  157. if (getClass() != obj.getClass()) {
  158. return false;
  159. }
  160. Cost other = (Cost) obj;
  161. if (cyclomaticCost != other.cyclomaticCost) {
  162. return false;
  163. }
  164. if (globalCost != other.globalCost) {
  165. return false;
  166. }
  167. if (!Arrays.equals(lodDistribution, other.lodDistribution)) {
  168. return false;
  169. }
  170. return true;
  171. }
  172. Map<String, Object> getAttributes() {
  173. Map<String, Object> atts = new HashMap<String, Object>();
  174. atts.put("cyclomatic", getCyclomaticComplexityCost());
  175. atts.put("global", getGlobalCost());
  176. atts.put("lod", getLoDSum());
  177. return atts;
  178. }
  179. public void addCyclomaticCost(int cyclomaticCost) {
  180. this.cyclomaticCost += cyclomaticCost;
  181. }
  182. public void addGlobalCost(int globalCost) {
  183. this.globalCost += globalCost;
  184. }
  185. public void addLodDistance(int distance) {
  186. add(Cost.lod(distance));
  187. }
  188. public boolean isEmpty() {
  189. return lodDistribution.length == 0 && cyclomaticCost == 0 && globalCost == 0;
  190. }
  191. public Cost negate() {
  192. int[] negativeLod = new int[lodDistribution.length];
  193. int index = 0;
  194. for (int lod : lodDistribution) {
  195. negativeLod[index++] = -lod;
  196. }
  197. return new Cost(-cyclomaticCost, -globalCost, negativeLod);
  198. }
  199. }