001: /*
002: * Copyright (c) 2001-2007, Jean Tessier
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * * Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * * Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in the
014: * documentation and/or other materials provided with the distribution.
015: *
016: * * Neither the name of Jean Tessier nor the names of his contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032:
033: package com.jeantessier.metrics;
034:
035: import java.io.*;
036: import java.util.*;
037:
038: import org.apache.log4j.*;
039:
040: /**
041: * Counts the number of submetrics according to selection
042: * criteria. If there are no criteria, it matches all
043: * submetrics. Each criterion is a boolean expression
044: * with measurement names, numbers, and boolean operators
045: * (<, <=, >, >=, ==, and !=). If a submetric matches
046: * any one of the expressions in the criteria, it is
047: * included in the count.
048: *
049: * <p>This is the syntax for initializing this type of
050: * measurement:</p>
051: *
052: * <pre>
053: * <init>
054: * (number | measurement name [DISPOSE_x]) [operator [(number | measurement name [DISPOSE_x])]]*
055: * ...
056: * </init>
057: * </pre>
058: */
059: public class NbSubMetricsMeasurement extends MeasurementBase {
060: private static final String OPERATORS = "/(<=)|(<)|(>=)|(>)|(==)|(!=)/";
061:
062: private static final String LESSER_THAN = "<";
063: private static final String LESSER_THAN_OR_EQUAL = "<=";
064: private static final String GREATER_THAN = ">";
065: private static final String GREATER_THAN_OR_EQUAL = ">=";
066: private static final String EQUALS = "==";
067: private static final String NOT_EQUALS = "!=";
068:
069: private static final double DELTA = 0.1;
070:
071: private List<String> terms = new LinkedList<String>();
072: private int value = 0;
073:
074: public NbSubMetricsMeasurement(MeasurementDescriptor descriptor,
075: Metrics context, String initText) {
076: super (descriptor, context, initText);
077:
078: try {
079: BufferedReader in = new BufferedReader(new StringReader(
080: initText));
081: String line;
082:
083: while ((line = in.readLine()) != null) {
084: terms.add(line.trim());
085: }
086:
087: in.close();
088: } catch (Exception ex) {
089: Logger.getLogger(getClass()).debug(
090: "Cannot initialize with \"" + initText + "\"", ex);
091: terms.clear();
092: }
093: }
094:
095: public List<String> getTerms() {
096: return terms;
097: }
098:
099: public void accept(MeasurementVisitor visitor) {
100: visitor.visitNbSubMetricsMeasurement(this );
101: }
102:
103: public boolean isEmpty() {
104: if (!isCached()) {
105: compute();
106: }
107:
108: return super .isEmpty();
109: }
110:
111: protected double compute() {
112: if (!isCached()) {
113: synchronized (this ) {
114: if (!isCached()) {
115: value = 0;
116:
117: if (getTerms().isEmpty()) {
118: value = getContext().getSubMetrics().size();
119: } else {
120: for (Metrics metrics : getContext()
121: .getSubMetrics()) {
122: if (getSelectMetrics(metrics)) {
123: value++;
124: }
125: }
126: }
127:
128: setEmpty(value == 0);
129:
130: setCached(true);
131: }
132: }
133: }
134:
135: return value;
136: }
137:
138: private boolean getSelectMetrics(Metrics metrics) {
139: boolean result = getTerms().isEmpty();
140:
141: Iterator<String> i = getTerms().iterator();
142: while (!result && i.hasNext()) {
143: result = evaluateTerm(i.next(), metrics);
144: }
145:
146: return result;
147: }
148:
149: private boolean evaluateTerm(String term, Metrics metrics) {
150: boolean result;
151:
152: Logger.getLogger(getClass()).debug(
153: "EvaluateTerm(\"" + term + "\", " + metrics + ")");
154:
155: List<String> elements = new ArrayList<String>();
156: perl().split(elements, OPERATORS, term);
157:
158: result = (elements.size() > 0) && ((elements.size() % 2) == 1);
159:
160: if (elements.size() == 1) {
161: result = metrics.hasMeasurement(elements.remove(0));
162: } else {
163: while (result && (elements.size() > 2)
164: && ((elements.size() % 2) == 1)) {
165: String leftString = elements.remove(0);
166: String operator = elements.remove(0);
167: String rightString = elements.get(0);
168:
169: double leftOperand = 0;
170: try {
171: leftOperand = Double.parseDouble(leftString);
172: } catch (NumberFormatException ex) {
173: try {
174: leftOperand = resolveOperand(leftString,
175: metrics);
176: } catch (NullPointerException ex2) {
177: result = false;
178: }
179: }
180:
181: double rightOperand = 0;
182: try {
183: rightOperand = Double.parseDouble(rightString);
184: } catch (NumberFormatException ex) {
185: try {
186: rightOperand = resolveOperand(rightString,
187: metrics);
188: } catch (NullPointerException ex2) {
189: result = false;
190: }
191: }
192:
193: if (result) {
194: if (operator.equals(LESSER_THAN)) {
195: result = leftOperand < rightOperand;
196: } else if (operator.equals(LESSER_THAN_OR_EQUAL)) {
197: result = leftOperand <= rightOperand;
198: } else if (operator.equals(GREATER_THAN)) {
199: result = leftOperand > rightOperand;
200: } else if (operator.equals(GREATER_THAN_OR_EQUAL)) {
201: result = leftOperand >= rightOperand;
202: } else if (operator.equals(EQUALS)) {
203: result = Math.abs(leftOperand - rightOperand) <= DELTA;
204: } else if (operator.equals(NOT_EQUALS)) {
205: result = Math.abs(leftOperand - rightOperand) > DELTA;
206: }
207: }
208: }
209: }
210:
211: Logger.getLogger(getClass()).debug(
212: "EvaluateTerm(\"" + term + "\", " + metrics + "): "
213: + result);
214:
215: return result;
216: }
217:
218: private double resolveOperand(String name, Metrics metrics) {
219: double result = 0;
220:
221: name = name.trim();
222:
223: Logger.getLogger(getClass()).debug(
224: "ResolveOperand(\"" + name + "\", " + metrics + ")");
225:
226: if (name.length() != 0) {
227: int dispose;
228:
229: synchronized (perl()) {
230: if (perl().match("/(.*)\\s+(dispose_\\w+)$/i", name)) {
231: name = perl().group(1);
232:
233: String disposeText = perl().group(2);
234:
235: if (disposeText.equalsIgnoreCase("DISPOSE_IGNORE")) {
236: dispose = StatisticalMeasurement.DISPOSE_IGNORE;
237: } else if (disposeText
238: .equalsIgnoreCase("DISPOSE_MINIMUM")) {
239: dispose = StatisticalMeasurement.DISPOSE_MINIMUM;
240: } else if (disposeText
241: .equalsIgnoreCase("DISPOSE_MEDIAN")) {
242: dispose = StatisticalMeasurement.DISPOSE_MEDIAN;
243: } else if (disposeText
244: .equalsIgnoreCase("DISPOSE_AVERAGE")) {
245: dispose = StatisticalMeasurement.DISPOSE_AVERAGE;
246: } else if (disposeText
247: .equalsIgnoreCase("DISPOSE_STANDARD_DEVIATION")) {
248: dispose = StatisticalMeasurement.DISPOSE_STANDARD_DEVIATION;
249: } else if (disposeText
250: .equalsIgnoreCase("DISPOSE_MAXIMUM")) {
251: dispose = StatisticalMeasurement.DISPOSE_MAXIMUM;
252: } else if (disposeText
253: .equalsIgnoreCase("DISPOSE_SUM")) {
254: dispose = StatisticalMeasurement.DISPOSE_SUM;
255: } else if (disposeText
256: .equalsIgnoreCase("DISPOSE_NB_DATA_POINTS")) {
257: dispose = StatisticalMeasurement.DISPOSE_NB_DATA_POINTS;
258: } else {
259: dispose = StatisticalMeasurement.DISPOSE_IGNORE;
260: }
261: } else {
262: dispose = StatisticalMeasurement.DISPOSE_IGNORE;
263: }
264: }
265:
266: Measurement measurement = metrics.getMeasurement(name);
267:
268: if (measurement instanceof StatisticalMeasurement) {
269: StatisticalMeasurement stats = (StatisticalMeasurement) measurement;
270:
271: switch (dispose) {
272: case StatisticalMeasurement.DISPOSE_MINIMUM:
273: result = stats.getMinimum();
274: break;
275: case StatisticalMeasurement.DISPOSE_MEDIAN:
276: result = stats.getMedian();
277: break;
278: case StatisticalMeasurement.DISPOSE_AVERAGE:
279: result = stats.getAverage();
280: break;
281: case StatisticalMeasurement.DISPOSE_STANDARD_DEVIATION:
282: result = stats.getStandardDeviation();
283: break;
284: case StatisticalMeasurement.DISPOSE_MAXIMUM:
285: result = stats.getMaximum();
286: break;
287: case StatisticalMeasurement.DISPOSE_SUM:
288: result = stats.getSum();
289: break;
290: case StatisticalMeasurement.DISPOSE_NB_DATA_POINTS:
291: result = stats.getNbDataPoints();
292: break;
293: case StatisticalMeasurement.DISPOSE_IGNORE:
294: default:
295: result = stats.doubleValue();
296: break;
297: }
298: } else if (measurement instanceof NullMeasurement) {
299: throw new NullPointerException();
300: } else {
301: result = measurement.doubleValue();
302: }
303: }
304:
305: Logger.getLogger(getClass()).debug(
306: "ResolveOperand(\"" + name + "\", " + metrics + "): "
307: + result);
308:
309: return result;
310: }
311: }
|