1 | /* |
2 | * Copyright (c) 2001-2009, Jean Tessier |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * * Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * |
12 | * * Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * * Neither the name of Jean Tessier nor the names of his contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR |
24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | package com.jeantessier.metrics; |
34 | |
35 | import java.io.*; |
36 | |
37 | import org.apache.log4j.*; |
38 | |
39 | /** |
40 | * <p>Divides one measurement (base) by another (divider). Both |
41 | * must be in the same context.</p> |
42 | * |
43 | * <p>This is the syntax for initializing this type of |
44 | * measurement:</p> |
45 | * |
46 | * <pre> |
47 | * <init> |
48 | * base measurement name [DISPOSE_x] |
49 | * divider measurement name [DISPOSE_x] |
50 | * </init> |
51 | * </pre> |
52 | * |
53 | * <p>If either is missing, this measurement will be NaN.</p> |
54 | */ |
55 | public class RatioMeasurement extends MeasurementBase { |
56 | private String baseName; |
57 | private int baseDispose; |
58 | private String dividerName; |
59 | private int dividerDispose; |
60 | |
61 | private double value = 0.0; |
62 | |
63 | public RatioMeasurement(MeasurementDescriptor descriptor, Metrics context, String initText) { |
64 | super(descriptor, context, initText); |
65 | |
66 | try { |
67 | BufferedReader in = new BufferedReader(new StringReader(initText)); |
68 | |
69 | synchronized (perl()) { |
70 | baseName = in.readLine().trim(); |
71 | if (perl().match("/(.*)\\s+(dispose_\\w+)$/i", baseName)) { |
72 | baseName = perl().group(1); |
73 | |
74 | String disposeText = perl().group(2); |
75 | |
76 | if (disposeText.equalsIgnoreCase("DISPOSE_IGNORE")) { |
77 | baseDispose = StatisticalMeasurement.DISPOSE_IGNORE; |
78 | } else if (disposeText.equalsIgnoreCase("DISPOSE_MINIMUM")) { |
79 | baseDispose = StatisticalMeasurement.DISPOSE_MINIMUM; |
80 | } else if (disposeText.equalsIgnoreCase("DISPOSE_MEDIAN")) { |
81 | baseDispose = StatisticalMeasurement.DISPOSE_MEDIAN; |
82 | } else if (disposeText.equalsIgnoreCase("DISPOSE_AVERAGE")) { |
83 | baseDispose = StatisticalMeasurement.DISPOSE_AVERAGE; |
84 | } else if (disposeText.equalsIgnoreCase("DISPOSE_STANDARD_DEVIATION")) { |
85 | baseDispose = StatisticalMeasurement.DISPOSE_STANDARD_DEVIATION; |
86 | } else if (disposeText.equalsIgnoreCase("DISPOSE_MAXIMUM")) { |
87 | baseDispose = StatisticalMeasurement.DISPOSE_MAXIMUM; |
88 | } else if (disposeText.equalsIgnoreCase("DISPOSE_SUM")) { |
89 | baseDispose = StatisticalMeasurement.DISPOSE_SUM; |
90 | } else if (disposeText.equalsIgnoreCase("DISPOSE_NB_DATA_POINTS")) { |
91 | baseDispose = StatisticalMeasurement.DISPOSE_NB_DATA_POINTS; |
92 | } else { |
93 | baseDispose = StatisticalMeasurement.DISPOSE_IGNORE; |
94 | } |
95 | } else { |
96 | baseDispose = StatisticalMeasurement.DISPOSE_IGNORE; |
97 | } |
98 | |
99 | dividerName = in.readLine().trim(); |
100 | if (perl().match("/(.*)\\s+(dispose_\\w+)$/i", dividerName)) { |
101 | dividerName = perl().group(1); |
102 | |
103 | String disposeText = perl().group(2); |
104 | |
105 | if (disposeText.equalsIgnoreCase("DISPOSE_IGNORE")) { |
106 | dividerDispose = StatisticalMeasurement.DISPOSE_IGNORE; |
107 | } else if (disposeText.equalsIgnoreCase("DISPOSE_MINIMUM")) { |
108 | dividerDispose = StatisticalMeasurement.DISPOSE_MINIMUM; |
109 | } else if (disposeText.equalsIgnoreCase("DISPOSE_MEDIAN")) { |
110 | dividerDispose = StatisticalMeasurement.DISPOSE_MEDIAN; |
111 | } else if (disposeText.equalsIgnoreCase("DISPOSE_AVERAGE")) { |
112 | dividerDispose = StatisticalMeasurement.DISPOSE_AVERAGE; |
113 | } else if (disposeText.equalsIgnoreCase("DISPOSE_STANDARD_DEVIATION")) { |
114 | dividerDispose = StatisticalMeasurement.DISPOSE_STANDARD_DEVIATION; |
115 | } else if (disposeText.equalsIgnoreCase("DISPOSE_MAXIMUM")) { |
116 | dividerDispose = StatisticalMeasurement.DISPOSE_MAXIMUM; |
117 | } else if (disposeText.equalsIgnoreCase("DISPOSE_SUM")) { |
118 | dividerDispose = StatisticalMeasurement.DISPOSE_SUM; |
119 | } else if (disposeText.equalsIgnoreCase("DISPOSE_NB_DATA_POINTS")) { |
120 | dividerDispose = StatisticalMeasurement.DISPOSE_NB_DATA_POINTS; |
121 | } else { |
122 | dividerDispose = StatisticalMeasurement.DISPOSE_IGNORE; |
123 | } |
124 | } else { |
125 | dividerDispose = StatisticalMeasurement.DISPOSE_IGNORE; |
126 | } |
127 | } |
128 | |
129 | in.close(); |
130 | } catch (Exception ex) { |
131 | Logger.getLogger(getClass()).debug("Cannot initialize with \"" + initText + "\"", ex); |
132 | baseName = null; |
133 | dividerName = null; |
134 | } |
135 | } |
136 | |
137 | public String getBaseName() { |
138 | return baseName; |
139 | } |
140 | |
141 | public int getBaseDispose() { |
142 | return baseDispose; |
143 | } |
144 | |
145 | public String getDividerName() { |
146 | return dividerName; |
147 | } |
148 | |
149 | public int getDividerDispose() { |
150 | return dividerDispose; |
151 | } |
152 | |
153 | public void accept(MeasurementVisitor visitor) { |
154 | visitor.visitRatioMeasurement(this); |
155 | } |
156 | |
157 | public boolean isEmpty() { |
158 | if (!isCached()) { |
159 | compute(); |
160 | } |
161 | |
162 | return super.isEmpty(); |
163 | } |
164 | |
165 | protected double compute() { |
166 | if (!isCached()) { |
167 | value = Double.NaN; |
168 | |
169 | if (getContext() != null && getBaseName() != null && getDividerName() != null) { |
170 | Measurement base = getContext().getMeasurement(getBaseName()); |
171 | Measurement divider = getContext().getMeasurement(getDividerName()); |
172 | |
173 | double baseValue = Double.NaN; |
174 | double dividerValue = Double.NaN; |
175 | |
176 | if (base instanceof StatisticalMeasurement) { |
177 | StatisticalMeasurement stats = (StatisticalMeasurement) base; |
178 | |
179 | switch (getBaseDispose()) { |
180 | case StatisticalMeasurement.DISPOSE_MINIMUM: |
181 | baseValue = stats.getMinimum(); |
182 | break; |
183 | case StatisticalMeasurement.DISPOSE_MEDIAN: |
184 | baseValue = stats.getMedian(); |
185 | break; |
186 | case StatisticalMeasurement.DISPOSE_AVERAGE: |
187 | baseValue = stats.getAverage(); |
188 | break; |
189 | case StatisticalMeasurement.DISPOSE_STANDARD_DEVIATION: |
190 | baseValue = stats.getStandardDeviation(); |
191 | break; |
192 | case StatisticalMeasurement.DISPOSE_MAXIMUM: |
193 | baseValue = stats.getMaximum(); |
194 | break; |
195 | case StatisticalMeasurement.DISPOSE_SUM: |
196 | baseValue = stats.getSum(); |
197 | break; |
198 | case StatisticalMeasurement.DISPOSE_NB_DATA_POINTS: |
199 | baseValue = stats.getNbDataPoints(); |
200 | break; |
201 | case StatisticalMeasurement.DISPOSE_IGNORE: |
202 | default: |
203 | baseValue = stats.getValue().doubleValue(); |
204 | break; |
205 | } |
206 | } else if (base != null) { |
207 | baseValue = base.getValue().doubleValue(); |
208 | } |
209 | |
210 | if (divider instanceof StatisticalMeasurement) { |
211 | StatisticalMeasurement stats = (StatisticalMeasurement) divider; |
212 | |
213 | switch (getDividerDispose()) { |
214 | case StatisticalMeasurement.DISPOSE_MINIMUM: |
215 | dividerValue = stats.getMinimum(); |
216 | break; |
217 | case StatisticalMeasurement.DISPOSE_MEDIAN: |
218 | dividerValue = stats.getMedian(); |
219 | break; |
220 | case StatisticalMeasurement.DISPOSE_AVERAGE: |
221 | dividerValue = stats.getAverage(); |
222 | break; |
223 | case StatisticalMeasurement.DISPOSE_STANDARD_DEVIATION: |
224 | dividerValue = stats.getStandardDeviation(); |
225 | break; |
226 | case StatisticalMeasurement.DISPOSE_MAXIMUM: |
227 | dividerValue = stats.getMaximum(); |
228 | break; |
229 | case StatisticalMeasurement.DISPOSE_SUM: |
230 | dividerValue = stats.getSum(); |
231 | break; |
232 | case StatisticalMeasurement.DISPOSE_NB_DATA_POINTS: |
233 | dividerValue = stats.getNbDataPoints(); |
234 | break; |
235 | case StatisticalMeasurement.DISPOSE_IGNORE: |
236 | default: |
237 | dividerValue = stats.getValue().doubleValue(); |
238 | break; |
239 | } |
240 | } else if (divider != null) { |
241 | dividerValue = divider.getValue().doubleValue(); |
242 | } |
243 | |
244 | value = baseValue / dividerValue; |
245 | } |
246 | |
247 | setEmpty(Double.isNaN(value) || Double.isInfinite(value)); |
248 | |
249 | setCached(true); |
250 | } |
251 | |
252 | return value; |
253 | } |
254 | } |