Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 226   Methods: 19
NCLOC: 133   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
TextLogSink.java 2.9% 3.9% 10.5% 4.6%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.debug;
 36   
 37    import java.text.DateFormat;
 38    import java.text.SimpleDateFormat;
 39    import java.util.Date;
 40   
 41    import edu.rice.cs.plt.iter.IterUtil;
 42    import edu.rice.cs.plt.iter.SizedIterable;
 43    import edu.rice.cs.plt.lambda.Lambda;
 44    import edu.rice.cs.plt.recur.RecurUtil;
 45    import edu.rice.cs.plt.recur.RecurUtil.ArrayStringMode;
 46    import edu.rice.cs.plt.text.TextUtil;
 47    import edu.rice.cs.plt.tuple.Option;
 48    import edu.rice.cs.plt.tuple.Pair;
 49   
 50    /**
 51    * A LogSink that formats its messages as lines of text. Subclasses are responsible for writing that text to
 52    * an appropriate location.
 53    */
 54    public abstract class TextLogSink implements LogSink {
 55   
 56    private static final SizedIterable<String> EMPTY_MESSAGE = IterUtil.singleton("");
 57    private static final SizedIterable<String> START_MESSAGE = IterUtil.singleton("Starting");
 58    private static final SizedIterable<String> END_MESSAGE = IterUtil.singleton("Ending");
 59    private static final SizedIterable<String> NO_STACK_MESSAGE = IterUtil.singleton("[No stack trace available]");
 60    private static final DateFormat TIME_FORMATTER = new SimpleDateFormat("H:mm:ss.SSS");
 61   
 62    private final int _idealLineWidth;
 63   
 64  18 protected TextLogSink() { _idealLineWidth = 120; }
 65   
 66  4 protected TextLogSink(int idealLineWidth) {
 67  0 if (idealLineWidth < 1) { throw new IllegalArgumentException("idealLineWidth < 1"); }
 68  4 _idealLineWidth = idealLineWidth;
 69    }
 70   
 71    /**
 72    * Record the given message, the body of which is described by the given lines of text. Subclasses should ensure
 73    * that the effects of this method appear to be atomic (at least within the context of concurrent invocations of
 74    * this method). This can be trivially achieved by declaring the method {@code synchronized}.
 75    */
 76    protected abstract void write(Message m, SizedIterable<String> text);
 77   
 78    /**
 79    * Record the given start message, the body of which is described by the given lines of text. Subclasses
 80    * should ensure that the effects of this method appear to be atomic (at least within the context of
 81    * concurrent invocations of this method). This can be trivially achieved by declaring the method {@code
 82    * synchronized}.
 83    */
 84    protected abstract void writeStart(StartMessage m, SizedIterable<String> text);
 85   
 86    /**
 87    * Record the given end message, the body of which is described by the given lines of text. Subclasses
 88    * should ensure that the effects of this method appear to be atomic (at least within the context of
 89    * concurrent invocations of this method). This can be trivially achieved by declaring the method {@code
 90    * synchronized}.
 91    */
 92    protected abstract void writeEnd(EndMessage m, SizedIterable<String> text);
 93   
 94    /** Convert a time to a string of the form {@code "12:23:03.013"} */
 95  0 protected static String formatTime(Date time) {
 96  0 return TIME_FORMATTER.format(time);
 97    }
 98   
 99    /**
 100    * Convert a thread to a string of the form {@code "Custom Thread 22"}, consisting of the thread's
 101    * name and id.
 102    */
 103  0 protected static String formatThread(ThreadSnapshot thread) {
 104  0 return "\"" + thread.getName() + "\" " + thread.getId();
 105    }
 106   
 107    /** Convert a location to a string of the form {@code "edu.rice.cs.plt.debug.TextLogSink.formatLocation(24)"} */
 108  0 protected static String formatLocation(Option<StackTraceElement> location) {
 109  0 if (location.isSome()) { return formatLocation(location.unwrap()); }
 110  0 else { return "[Unknown location]"; }
 111    }
 112   
 113    /** Convert a location to a string of the form {@code "edu.rice.cs.plt.debug.TextLogSink.formatLocation(24)"} */
 114  0 protected static String formatLocation(StackTraceElement location) {
 115  0 StringBuilder result = new StringBuilder();
 116  0 result.append(location.getClassName());
 117  0 result.append(".");
 118  0 result.append(location.getMethodName());
 119  0 result.append("(");
 120  0 int line = location.getLineNumber();
 121  0 if (line >= 0) { result.append(line); }
 122  0 else if (location.isNativeMethod()) { result.append("native"); }
 123  0 else { result.append("unknown"); }
 124  0 result.append(")");
 125  0 return result.toString();
 126    }
 127   
 128   
 129  0 public void log(StandardMessage m) {
 130  0 SizedIterable<String> text = IterUtil.compose(processText(m.text()), processValues(m.values()));
 131  0 if (IterUtil.isEmpty(text)) { text = EMPTY_MESSAGE; }
 132  0 write(m, text);
 133    }
 134   
 135  0 public void logStart(StartMessage m) {
 136  0 SizedIterable<String> text;
 137  0 if (m.text().isNone()) { text = START_MESSAGE; }
 138  0 else { text = processString("Start " + m.text().unwrap()); }
 139  0 text = IterUtil.compose(text, processValues(m.values()));
 140  0 writeStart(m, text);
 141    }
 142   
 143  0 public void logEnd(EndMessage m) {
 144  0 SizedIterable<String> text;
 145  0 if (m.text().isNone()) { text = END_MESSAGE; }
 146  0 else { text = processString("End " + m.text().unwrap()); }
 147  0 text = IterUtil.compose(text, processValues(m.values()));
 148  0 writeEnd(m, text);
 149    }
 150   
 151  0 public void logError(ErrorMessage m) {
 152  0 SizedIterable<String> text = IterUtil.compose(processText(m.text()), processThrowable(m.error()));
 153  0 write(m, text);
 154    }
 155   
 156  0 public void logStack(StackMessage m) {
 157  0 SizedIterable<String> text = IterUtil.compose(processText(m.text()), processStack(m.stack()));
 158  0 if (IterUtil.isEmpty(text)) { text = NO_STACK_MESSAGE; }
 159  0 write(m, text);
 160    }
 161   
 162  0 private SizedIterable<String> processText(Option<String> text) {
 163  0 if (text.isSome()) { return processString(text.unwrap()); }
 164  0 else { return IterUtil.empty(); }
 165    }
 166   
 167  0 private SizedIterable<String> processThrowable(Throwable t) {
 168  0 return processThrowable(t, false);
 169    }
 170   
 171  0 private SizedIterable<String> processThrowable(Throwable t, boolean asCause) {
 172  0 if (t == null) { return IterUtil.singleton("null"); }
 173  0 SizedIterable<String> result;
 174  0 if (asCause) { result = IterUtil.make("", "Caused by " + t, "at"); }
 175  0 else { result = IterUtil.make(t.toString(), "at"); }
 176  0 result = IterUtil.compose(result, processStack(IterUtil.asIterable(t.getStackTrace())));
 177  0 if (t.getCause() != null) {
 178  0 result = IterUtil.compose(result, processThrowable(t.getCause()));
 179    }
 180  0 return result;
 181    }
 182   
 183  0 private SizedIterable<String> processStack(Iterable<StackTraceElement> stack) {
 184  0 SizedIterable<String> result = IterUtil.empty();
 185  0 for (StackTraceElement e : stack) {
 186  0 result = IterUtil.compose(result, e.toString());
 187    }
 188  0 return result;
 189    }
 190   
 191  0 private SizedIterable<String> processValues(Iterable<Pair<String, Object>> vals) {
 192  0 return IterUtil.collapse(IterUtil.mapSnapshot(vals, _processValue));
 193    }
 194   
 195    private final Lambda<Pair<String, Object>, SizedIterable<String>> _processValue =
 196    new Lambda<Pair<String, Object>, SizedIterable<String>>() {
 197  0 public SizedIterable<String> value(Pair<String, Object> p) { return processValue(p.first(), p.second()); }
 198    };
 199   
 200  0 private SizedIterable<String> processValue(String name, Object value) {
 201  0 SizedIterable<String> valStrings = processString(RecurUtil.safeToString(value));
 202  0 if (valStrings.size() > 1 || IterUtil.first(valStrings).length() > _idealLineWidth) {
 203    // if this is an array or Iterable, print values on separate lines
 204  0 if (value instanceof Iterable<?>) {
 205  0 valStrings = processString(IterUtil.multilineToString((Iterable<?>) value));
 206    }
 207  0 else if (value instanceof Object[]) {
 208  0 valStrings = processString(RecurUtil.arrayToString((Object[]) value, ArrayStringMode.SHALLOW_MULTILINE));
 209    }
 210    }
 211  0 if (valStrings.size() == 1) {
 212  0 return IterUtil.singleton(name + ": " + IterUtil.first(valStrings));
 213    }
 214    else {
 215  0 return IterUtil.compose(name + ":", valStrings);
 216    }
 217    }
 218   
 219    /** Convert the string to a list of lines. Result has size {@code >= 1}. */
 220  0 private static SizedIterable<String> processString(String s) {
 221  0 SizedIterable<String> result = TextUtil.getLines(s);
 222  0 if (result.size() == 0) { return EMPTY_MESSAGE; }
 223  0 else { return result; }
 224    }
 225   
 226    }