1 // Copyright (C) 2000 - 2011 Philip Aston 2 // All rights reserved. 3 // 4 // This file is part of The Grinder software distribution. Refer to 5 // the file LICENSE which is part of The Grinder distribution for 6 // licensing details. The Grinder distribution is available on the 7 // Internet at http://grinder.sourceforge.net/ 8 // 9 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 12 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 13 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 14 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 15 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 16 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 17 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 18 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 19 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 20 // OF THE POSSIBILITY OF SUCH DAMAGE. 21 22 package net.grinder.statistics; 23 24 25 /** 26 * <p> 27 * Associate a statistic expression with display information. 28 * </p> 29 * 30 * <p> 31 * Statistic expressions are composed of statistic names (see 32 * {@link StatisticsIndexMap}) in a simple post-fix format using the symbols 33 * <code>+</code>, <code>-</code>, <code>/</code> and <code>*</code>, 34 * which have their usual meanings, in conjunction with simple statistic names 35 * or sub-expressions. Precedence can be controlled by grouping expressions in 36 * parentheses. For example, the error rate is 37 * <code>(* (/ errors period) 1000)</code> errors per second. 38 * </p> 39 * 40 * <p> 41 * Sample statistics, such as <em>timedTests</em>, must be introduced with 42 * one of <code>sum</code>, <code>count</code>, or <code>variance</code>, 43 * depending on the attribute of interest. 44 * </p> 45 * 46 * <p> 47 * For example, the statistic expression <code>(/ (sum timedTests) 48 * (count timedTests))</code> 49 * represents the mean test time in milliseconds. 50 * 51 * @author Philip Aston 52 */ 53 public final class ExpressionView { 54 55 private static int s_creationOrder; 56 57 private final String m_displayName; 58 private final String m_expressionString; 59 private final boolean m_showForCompositeStatistics; 60 private final int m_hashCode; 61 private final int m_creationOrder; 62 63 private final StatisticExpression m_expression; 64 65 ExpressionView(String displayName, 66 String expressionString, 67 StatisticExpression expression, 68 boolean showForCompositeStatistics) { 69 m_displayName = displayName; 70 m_expressionString = expressionString; 71 m_showForCompositeStatistics = showForCompositeStatistics; 72 m_expression = expression; 73 74 m_hashCode = 75 m_displayName.hashCode() ^ 76 (expressionString != null ? m_expressionString.hashCode() : 0); 77 78 // Code outside this package can only obtain ExpressionViews through a 79 // StatisticExpressionFactory instance, and in turn this factory must be 80 // obtained from the singleton StatisticServices instance. 81 // StatisticsServicesImplementation creates the common statistics views in 82 // a static block, so we can rely on creation order to ensure that the 83 // standard views are ordered before script defined views. 84 synchronized (ExpressionView.class) { 85 m_creationOrder = s_creationOrder++; 86 } 87 } 88 89 /** 90 * Get the common display name. 91 * 92 * @return The display name. 93 */ 94 public String getDisplayName() { 95 return m_displayName; 96 } 97 98 /** 99 * Return the {@link StatisticExpression}. 100 * 101 * @return The {@link StatisticExpression}. 102 */ 103 public StatisticExpression getExpression() { 104 return m_expression; 105 } 106 107 /** 108 * Return the expression string. 109 * 110 * @return The string, or <code>null</code> if this view was built directly 111 * from a {@link StatisticExpression}. 112 */ 113 public String getExpressionString() { 114 return m_expressionString; 115 } 116 117 /** 118 * Return whether this view is relevant for totals of composite statistics. 119 * Many views (particularly those representing aggregate time, or averages) 120 * are ambiguous for composite statistics, so we don't show them. 121 * 122 * @return <code>true</code> => show this view for composite statistics. 123 */ 124 public boolean getShowForCompositeStatistics() { 125 return m_showForCompositeStatistics; 126 } 127 128 /** 129 * Value based equality. 130 * 131 * @param other An <code>Object</code> to compare. 132 * @return <code>true</code> => <code>other</code> is equal to this object. 133 */ 134 public boolean equals(Object other) { 135 if (other == this) { 136 return true; 137 } 138 139 if (other == null || other.getClass() != ExpressionView.class) { 140 return false; 141 } 142 143 final ExpressionView otherView = (ExpressionView)other; 144 145 return 146 m_hashCode == otherView.m_hashCode && 147 m_displayName.equals(otherView.m_displayName) && 148 149 // If either expression string is null, one of the views 150 // is not externalisable. If so, we only compare on the 151 // display names. 152 (m_expressionString == null || 153 otherView.m_expressionString == null || 154 m_expressionString.equals(otherView.m_expressionString)); 155 } 156 157 /** 158 * Implement {@link Object#hashCode}. 159 * 160 * @return an <code>int</code> value 161 */ 162 public int hashCode() { 163 return m_hashCode; 164 } 165 166 /** 167 * Return a <code>String</code> representation of this 168 * <code>ExpressionView</code>. 169 * 170 * @return The <code>String</code> 171 */ 172 public String toString() { 173 final StringBuilder result = new StringBuilder(32); 174 result.append("ExpressionView("); 175 result.append(m_displayName); 176 177 if (m_expressionString != null) { 178 result.append(", "); 179 result.append(m_expressionString); 180 } 181 182 result.append(")"); 183 184 return result.toString(); 185 } 186 187 int getCreationOrder() { 188 return m_creationOrder; 189 } 190 }