001: /*
002: * @(#)DirectoryChannelLogger.java
003: *
004: * Copyright (C) 2002-2004 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.logger;
028:
029: import java.io.File;
030: import java.io.FileWriter;
031: import java.io.IOException;
032:
033: import net.sourceforge.groboutils.codecoverage.v2.IChannelLogger;
034:
035: /**
036: * Logs coverage reports to a directory of logs. The directories are split
037: * by the channel index, and the directory contains one log per class file
038: * analyzed.
039: * <p>
040: * As of 2004-Jun-3, the output format has changed to:
041: * <PRE>
042: * <i>method index</i> <i>mark index</i> EOL
043: * </PRE>
044: * where both indices are output in hexidecimal format. This is for performance
045: * reasons.
046: *
047: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
048: * @version $Date: 2004/07/07 09:39:10 $
049: * @since December 17, 2002
050: */
051: public class DirectoryChannelLogger implements IChannelLogger {
052: public static final String CLASS_LOG_EXTENTION = ".class.log";
053:
054: protected File baseDir;
055:
056: public DirectoryChannelLogger(File baseDir) {
057: // basedir can be null!
058: this .baseDir = baseDir;
059: if (baseDir == null) {
060: System.err
061: .println("DirectoryLogger base directory is null.");
062: } else {
063: if (!baseDir.exists()) {
064: baseDir.mkdir();
065: }
066: }
067: }
068:
069: /**
070: * Records a coverage of a marked bytecode instruction. This method should
071: * never throw an exception.
072: *
073: * @param classSignature a signature of the class file being covered.
074: * this signature includes the fully-qualified name of the class,
075: * along with a checksum to uniquely identify it.
076: * @param methodIndex index for a method within the class. The meta-data
077: * store will know how to translate the index to a method signature.
078: * @param markIndex the index of the bytecode instruction mark for this
079: * particular channel.
080: */
081: public void cover(String classSignature, short methodIndex,
082: short markIndex) {
083: if (this .baseDir != null) {
084: //System.err.println("DirectoryChannelLogger: "+classSignature+":"+methodIndex+"."+markIndex);
085: File f = getClassFile(this .baseDir, classSignature);
086: FileWriter fw = null;
087: try {
088: char[] out = createCoverString(methodIndex, markIndex);
089: synchronized (this ) {
090: fw = new FileWriter(f.toString(), true);
091: fw.write(out);
092: fw.flush();
093: //System.out.println("** wrote ["+out+"] to "+f.getAbsolutePath());
094: }
095: } catch (IOException ioe) {
096: // gasp!!!!
097: ioe.printStackTrace();
098:
099: // prevent these errors from occuring again
100: this .baseDir = null;
101: } finally {
102: if (fw != null) {
103: try {
104: fw.close();
105: } catch (IOException ioe) {
106: // gasp again!
107: ioe.printStackTrace();
108:
109: // this this happened on a close, I'll ignore it
110: }
111: }
112: }
113: }
114: }
115:
116: private static final char[] HEX = { '0', '1', '2', '3', '4', '5',
117: '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
118:
119: /**
120: * Make static final so that the invocation time is minimized.
121: * <p>
122: * This now returns a character array, for performance reasons. The
123: * array's format is in hexidecimal.
124: */
125: protected static final char[] createCoverString(short methodIndex,
126: short markIndex) {
127: char c[] = new char[10];
128: c[9] = '\n';
129:
130: // avoid the integer promotion later on,
131: // and do the masking with the HEX index lookup
132: int imeth = (int) methodIndex;
133: int imark = (int) markIndex;
134:
135: // unroll the loop
136:
137: // make sure we do a bitwise shift, not an arithmetic shift.
138: c[8] = HEX[imark & 0xf];
139: imark >>>= 4;
140: c[7] = HEX[imark & 0xf];
141: imark >>>= 4;
142: c[6] = HEX[imark & 0xf];
143: imark >>>= 4;
144: c[5] = HEX[imark & 0xf];
145:
146: c[4] = ' ';
147:
148: c[3] = HEX[imeth & 0xf];
149: imeth >>>= 4;
150: c[2] = HEX[imeth & 0xf];
151: imeth >>>= 4;
152: c[1] = HEX[imeth & 0xf];
153: imeth >>>= 4;
154: c[0] = HEX[imeth & 0xf];
155:
156: return c;
157: }
158:
159: /**
160: * Make static final so that the invocation time is minimized.
161: */
162: protected static final File getClassFile(File basedir,
163: String classSignature) {
164: return new File(basedir, classSignature + CLASS_LOG_EXTENTION);
165: }
166: }
|