001: /*
002: * @(#)ParseCoverageLogger.java
003: *
004: * Copyright (C) 2002-2003 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.compiler;
028:
029: import java.lang.reflect.Method;
030: import java.lang.reflect.Modifier;
031:
032: import net.sourceforge.groboutils.codecoverage.v2.logger.ICoverageLoggerConst;
033:
034: import org.apache.bcel.generic.ObjectType;
035: import org.apache.bcel.generic.Type;
036:
037: /**
038: * Parses the CoverageLogger class to discover the BCEL compatible invocation
039: * objects and names.
040: *
041: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
042: * @version $Date: 2004/04/15 05:48:25 $
043: * @since December 17, 2002
044: */
045: public class ParseCoverageLogger {
046: //private static final Class COVERAGELOGGER_CLASS = CoverageLogger.class;
047: /**
048: * This is not the simplest or safest way to default to the coverage
049: * logger, but it does prevent loading the coverage logger class, thus
050: * preventing a possible error message.
051: */
052: private static final String DEFAULT_COVERAGELOGGER_CLASSNAME = ICoverageLoggerConst.COVERAGELOGGER_CLASSNAME;
053: private static final String DEFAULT_COVERAGELOGGER_METHODNAME = ICoverageLoggerConst.INVOKE_METHOD_NAME;
054: private static final Class COVERAGE_SIGNATURE[] = ICoverageLoggerConst.COVERAGE_SIGNATURE;
055: private static final Class COVERAGE_RETURNTYPE = ICoverageLoggerConst.COVERAGE_RETURNTYPE;
056:
057: private String methodSignature;
058: private String methodName;
059: private String className;
060:
061: /**
062: * Reference the default CoverageLogger for creation.
063: */
064: public ParseCoverageLogger() {
065: // We will assume that the defaults given are correct. If they
066: // aren't valid, then this is a major bug in the logger, not
067: // this class.
068: storeData(DEFAULT_COVERAGELOGGER_CLASSNAME,
069: DEFAULT_COVERAGELOGGER_METHODNAME);
070: }
071:
072: /**
073: * Discovers the method signature and accompanying information for a
074: * specific coverage logger to generate. The given method must exist
075: * with the correct parameters, attributes, and return type.
076: * Otherwise, an exception is raised.
077: */
078: public ParseCoverageLogger(Class coverageClass, String methodName) {
079: if (methodName == null || coverageClass == null) {
080: throw new IllegalArgumentException("No null args.");
081: }
082: parseCoverageLoggerType(coverageClass, methodName);
083:
084: storeData(coverageClass.getName(), methodName);
085: }
086:
087: /**
088: * Inner method to store the data of the class and method for the
089: * logger. It doesn't do any checking besides null. Thus, this
090: * method must be private to prevent inadvertent invalid class and
091: * method assignment.
092: */
093: private void storeData(String className, String methodName) {
094: this .methodName = methodName;
095: this .className = className;
096: if (methodName == null || className == null) {
097: throw new IllegalArgumentException("No null args.");
098: }
099:
100: this .methodSignature = createSignature(COVERAGE_SIGNATURE,
101: COVERAGE_RETURNTYPE);
102: if (this .methodSignature == null) {
103: throw new IllegalStateException(
104: "Returned null method signature.");
105: }
106: }
107:
108: /**
109: * Returns the fully-qualified class name for the CoverageLogger.
110: */
111: public String getClassName() {
112: return this .className;
113: }
114:
115: /**
116: * Returns the name of the method, without the signature.
117: */
118: public String getMethodName() {
119: return this .methodName;
120: }
121:
122: /**
123: * Returns the signature of the method, without the method name.
124: */
125: public String getMethodSignature() {
126: return this .methodSignature;
127: }
128:
129: private void parseCoverageLoggerType(Class clazz, String methodName) {
130: // ensure that the method exists with the correct signature.
131: Method m = null;
132: try {
133: m = clazz.getMethod(methodName, COVERAGE_SIGNATURE);
134: } catch (NoSuchMethodException nsme) {
135: // this will throw the next exception. Hide this one!
136: m = null;
137: }
138:
139: if (m == null) {
140: throw new IllegalStateException(clazz.getName()
141: + " doesn't have a method named '" + methodName
142: + "'");
143: }
144:
145: // the method MUST NOT return anything!
146: if (m.getReturnType() != null
147: && !m.getReturnType().equals(COVERAGE_RETURNTYPE)) {
148: throw new IllegalStateException(clazz.getName()
149: + " method '" + methodName
150: + "' incorrectly has a return value.");
151: }
152:
153: // the method must also be static & public
154: int mod = m.getModifiers();
155: if (!Modifier.isStatic(mod) || !Modifier.isPublic(mod)) {
156: throw new IllegalStateException(clazz.getName()
157: + " method '" + methodName
158: + "' is not public or static.");
159: }
160: }
161:
162: private static String createSignature(Class signature[],
163: Class returnClass) {
164: Type returnType = getType(returnClass);
165: Type args[] = new Type[signature.length];
166: for (int i = 0; i < signature.length; ++i) {
167: args[i] = getType(signature[i]);
168: }
169: return Type.getMethodSignature(returnType, args);
170: }
171:
172: private static Type getType(Class c) {
173: if (c.equals(Void.TYPE)) {
174: return Type.VOID;
175: }
176: if (c.equals(Byte.TYPE)) {
177: return Type.BYTE;
178: }
179: if (c.equals(Character.TYPE)) {
180: return Type.CHAR;
181: }
182: if (c.equals(Short.TYPE)) {
183: return Type.SHORT;
184: }
185: if (c.equals(Integer.TYPE)) {
186: return Type.INT;
187: }
188: if (c.equals(Long.TYPE)) {
189: return Type.LONG;
190: }
191: if (c.equals(Float.TYPE)) {
192: return Type.FLOAT;
193: }
194: if (c.equals(Double.TYPE)) {
195: return Type.DOUBLE;
196: }
197: // else
198: return new ObjectType(c.getName());
199: }
200: }
|