001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2006, Arno Unkrig
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above
014: * copyright notice, this list of conditions and the following
015: * disclaimer in the documentation and/or other materials
016: * provided with the distribution.
017: * 3. The name of the author may not be used to endorse or promote
018: * products derived from this software without specific prior
019: * written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
032: */
033:
034: package org.codehaus.janino.util;
035:
036: import java.util.*;
037:
038: /**
039: * Implements a scheme for benchmarking, i.e. for determining and/or reporting the time elapsed
040: * between the beginning and the end of an activity.
041: * <p>
042: * The measurement is done by invoking {@link #begin()} and later calling {@link #end()} whichs
043: * returns the time elapsed since the call to {@link #begin()}.
044: * <p>
045: * Notice that calls to {@link #begin()} and {@link #end()} can be nested, and each call to
046: * {@link #end()} refers to the matching {@link #begin()} call. To ensure that all calls match,
047: * the preferred way to write a benchmark is
048: * <pre>
049: * ...
050: * Benchmark b = new Benchmark();
051: * ...
052: * b.begin();
053: * try {
054: * ....
055: * } finally {
056: * long ms = b.end();
057: * }
058: * </pre>
059: * This code layout also makes it visually easy to write correct pairs of {@link #begin()} /
060: * {@link #end()} pairs.
061: * <p>
062: * The pair {@link #beginReporting()} and {@link #endReporting()} do basically the same, but
063: * report the benchmarking information through an internal {@link Reporter} object. The default
064: * {@link Reporter} prints its messages by <code>System.out.println()</code>.
065: * <p>
066: * Reporting is only enabled if the Benchmark object was created through {@link #Benchmark(boolean)}
067: * with a <code>true</code> argument.
068: */
069: public class Benchmark {
070: private final Stack beginTimes = new Stack(); // Long
071:
072: public Benchmark() {
073: this .reportingEnabled = false;
074: this .reporter = null;
075: }
076:
077: /**
078: * @see Benchmark
079: */
080: public void begin() {
081: this .beginTimes.push(new Long(System.currentTimeMillis()));
082: }
083:
084: /**
085: * @see Benchmark
086: */
087: public long end() {
088: return System.currentTimeMillis()
089: - ((Long) this .beginTimes.pop()).longValue();
090: }
091:
092: // Reporting-related methods and fields.
093:
094: /**
095: * Set up a {@link Benchmark} with a default {@link Reporter} that reports to
096: * <code>System.out</code>.
097: */
098: public Benchmark(boolean reportingEnabled) {
099: this .reportingEnabled = reportingEnabled;
100: this .reporter = new Reporter() {
101: public void report(String message) {
102: System.out.println(message);
103: }
104: };
105: }
106:
107: /**
108: * Set up a {@link Benchmark} with a custom {@link Reporter}.
109: */
110: public Benchmark(boolean reportingEnabled, Reporter reporter) {
111: this .reportingEnabled = reportingEnabled;
112: this .reporter = reporter;
113: }
114:
115: private final boolean reportingEnabled;
116: private final Reporter reporter;
117:
118: /**
119: * Interface used to report messages.
120: */
121: public interface Reporter {
122: void report(String message);
123: }
124:
125: /**
126: * Begin a benchmark (see {@link #begin()}) and report the fact.
127: */
128: public void beginReporting() {
129: if (!this .reportingEnabled)
130: return;
131:
132: this .reportIndented("Beginning...");
133: this .begin();
134: }
135:
136: /**
137: * Begin a benchmark (see {@link #begin()}) and report the fact.
138: */
139: public void beginReporting(String message) {
140: if (!this .reportingEnabled)
141: return;
142: this .reportIndented(message + "...");
143: this .begin();
144: }
145:
146: /**
147: * End a benchmark (see {@link #end()}) and report the fact.
148: */
149: public void endReporting() {
150: if (!this .reportingEnabled)
151: return;
152: this .reportIndented("... took " + this .end() + " ms");
153: }
154:
155: /**
156: * End a benchmark (see {@link #begin()}) and report the fact.
157: */
158: public void endReporting(String message) {
159: if (!this .reportingEnabled)
160: return;
161: this .reportIndented("... took " + this .end() + " ms: "
162: + message);
163: }
164:
165: /**
166: * Report the given message.
167: */
168: public void report(String message) {
169: if (!this .reportingEnabled)
170: return;
171: this .reportIndented(message);
172: }
173:
174: /**
175: * Report the <code>title</code>, a colon, a space, and the pretty-printed
176: * {@link Object}.
177: * @param optionalTitle
178: * @param o
179: */
180: public void report(String optionalTitle, Object o) {
181: if (!this .reportingEnabled)
182: return;
183:
184: String prefix = optionalTitle == null ? "" : (optionalTitle
185: + ": " + (optionalTitle.length() < Benchmark.PAD
186: .length() ? Benchmark.PAD.substring(optionalTitle
187: .length()) : ""));
188:
189: if (o == null) {
190: this .reportIndented(prefix + "(undefined)");
191: } else if (o.getClass().isArray()) {
192: Object[] oa = (Object[]) o;
193: if (oa.length == 0) {
194: this .reportIndented(prefix + "(empty)");
195: } else if (oa.length == 1) {
196: this .reportIndented(prefix + oa[0].toString());
197: } else {
198: this .reportIndented(optionalTitle == null ? "Array:"
199: : optionalTitle + ':');
200: this .begin();
201: try {
202: for (int i = 0; i < oa.length; ++i)
203: this .report(null, oa[i]);
204: } finally {
205: this .end();
206: }
207: }
208: } else {
209: this .reportIndented(prefix + o.toString());
210: }
211: }
212:
213: private static final String PAD = " ";
214:
215: /**
216: * Report a message through {@link #reporter}, indent by N spaces where N is the current
217: * benchmark stack depth.
218: */
219: private void reportIndented(String message) {
220: StringBuffer sb = new StringBuffer();
221: for (int i = this .beginTimes.size(); i > 0; --i)
222: sb.append(" ");
223: sb.append(message);
224: this.reporter.report(sb.toString());
225: }
226: }
|