You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mz...@apache.org on 2006/03/16 21:07:17 UTC
svn commit: r386448 [2/2] - in /db/jdo/trunk/tck20: ./ src/conf/
src/java/org/apache/jdo/tck/api/ src/java/org/apache/jdo/tck/util/signature/
Added: db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/SignatureVerifier.java
URL: http://svn.apache.org/viewcvs/db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/SignatureVerifier.java?rev=386448&view=auto
==============================================================================
--- db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/SignatureVerifier.java (added)
+++ db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/SignatureVerifier.java Thu Mar 16 12:06:59 2006
@@ -0,0 +1,1278 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jdo.tck.util.signature;
+
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.TreeSet;
+import java.util.HashSet;
+
+import java.text.ParseException;
+
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileReader;
+import java.io.LineNumberReader;
+
+
+/**
+ * Tests classes for correct signatures.
+ *
+ * @author Martin Zaun
+ */
+public class SignatureVerifier {
+ /** The new-line character on this system. */
+ static protected final String NL = System.getProperty("line.separator");
+
+ /** A writer for standard output. */
+ protected final PrintWriter log;
+
+ /** Flag to print error output only. */
+ private boolean quiet;
+
+ /** Flag to also print verbose output. */
+ private boolean verbose;
+
+ /** The parse to be used for parsing signature descriptor files. */
+ protected final Parser parser = new Parser();
+
+ /** The classloader to be used for loading types. */
+ protected final ClassLoader classLoader;
+
+ /** The currently tested Class. */
+ protected Class cls;
+
+ /** All untested, declared members of the current class. */
+ protected final Set members = new HashSet();
+
+ /** Collects names of loadable classes. */
+ private final Set loading = new TreeSet();
+
+ /** Collects names of unloadable classes. */
+ private final Set notLoading = new TreeSet();
+
+ /** Counts tested features (class, constructor, fields, methods). */
+ private int tested;
+
+ /** Counts missing members (constructor, fields, methods). */
+ private int missing;
+
+ /** Counts non-matching features (class, constructor, fields, methods). */
+ private int mismatch;
+
+ /** Counts matching features (class, constructor, fields, methods). */
+ private int matching;
+
+ /** Counts public non-standard members (constructor, fields, methods). */
+ private int nonStandard;
+
+ /** Counts other reported problems (e.g., accessing field values). */
+ private int otherProblems;
+
+ /** Constructs a test instance. */
+ public SignatureVerifier(ClassLoader loader, PrintWriter log,
+ boolean quiet, boolean verbose) {
+ classLoader = this.getClass().getClassLoader();
+ this.log = log;
+ this.quiet = quiet;
+ this.verbose = (!quiet && verbose);
+ }
+
+ /** Constructs a test instance. */
+ public SignatureVerifier(PrintWriter log,
+ boolean quiet, boolean verbose) {
+ this(SignatureVerifier.class.getClassLoader(), log, quiet, verbose);
+ }
+
+ // ----------------------------------------------------------------------
+ // Local Logging Methods
+ // ----------------------------------------------------------------------
+
+ /** Prints an error message. */
+ protected void logError(String msg) {
+ log.println(msg);
+ }
+
+ /** Prints an info message. */
+ protected void logInfo(String msg) {
+ if (!quiet) {
+ log.println(msg);
+ }
+ }
+
+ /** Prints a verbose message. */
+ protected void logVerbose(String msg) {
+ if (verbose) {
+ log.println(msg);
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Test Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Tests the signature of classes (in the specified classloader) against
+ * a list of signature descriptor files; returns with a status code.
+ * @param descrFileNames list of signature descriptor file names
+ * @return zero if all tests have passed and no problems were detected
+ */
+ public int test(List descrFileNames)
+ throws IOException, ParseException {
+ // check argument
+ if (descrFileNames == null || descrFileNames.isEmpty()) {
+ final String m = ("ERROR: No signature descriptor file to parse.");
+ logError(m);
+ return -1;
+ }
+
+ // clear statistics
+ loading.clear();
+ notLoading.clear();
+ tested = 0;
+ missing = 0;
+ mismatch = 0;
+ matching = 0;
+ nonStandard = 0;
+ otherProblems = 0;
+
+ // process descriptor files
+ parser.parse(descrFileNames);
+ report();
+
+ // return a positive value in case of any problems
+ return (notLoading.size() + missing + mismatch + nonStandard
+ + otherProblems);
+ }
+
+ /**
+ * Reports the results of the last signature test run.
+ */
+ public void report() {
+ logInfo("");
+ logInfo("Signature Test Results");
+ logInfo("======================");
+ logInfo("");
+ logInfo(" tested features: " + tested);
+ logInfo("");
+ logInfo("Successes:");
+ logInfo(" matching features: " + matching);
+ logInfo(" loadable classes: " + loading.size());
+ logInfo("");
+ logInfo("Failures:");
+ logInfo(" missing features: " + missing);
+ logInfo(" non-matching features: " + mismatch);
+ logInfo(" non-standard features: " + nonStandard);
+ logInfo(" unloadable classes: " + notLoading.size());
+ logInfo(" other problems: " + otherProblems);
+ logInfo("");
+ }
+
+ // ----------------------------------------------------------------------
+ // Test Logic
+ // ----------------------------------------------------------------------
+
+ /** Handles class loading problems. */
+ protected void handleNotLoading(Throwable t) {
+ notLoading.add(t.getMessage().replace('/', '.'));
+ final String m = ("--- failed loading class;" + NL
+ + " caught: " + t);
+ logError(m);
+ }
+
+ /** Handles missing members. */
+ protected void handleMissing(String msg, String exp) {
+ missing++;
+ final String m = ("--- " + msg + NL
+ + " expected: " + exp + NL
+ + " class: " + Formatter.toString(cls));
+ logError(m);
+ }
+
+ /** Handles non-matching features. */
+ protected void handleMismatch(String msg, String exp, String fnd) {
+ mismatch++;
+ final String m = ("--- " + msg + NL
+ + " expected: " + exp + NL
+ + " found: " + fnd + NL
+ + " class: " + Formatter.toString(cls));
+ logError(m);
+ }
+
+ /** Handles public non-standard features. */
+ protected void handleNonStandard(String msg, String fnd) {
+ nonStandard++;
+ final String m = ("--- " + msg + NL
+ + " found: " + fnd + NL
+ + " class: " + Formatter.toString(cls));
+ logError(m);
+ }
+
+ /** Handles other problems. */
+ protected void handleProblem(String msg, String exp) {
+ otherProblems++;
+ final String m = ("--- " + msg + NL
+ + " expected: " + exp + NL
+ + " class: " + Formatter.toString(cls));
+ logError(m);
+ }
+
+ /** Handles a perfect feature match. */
+ protected void handleMatch(String msg, String fnd) {
+ matching++;
+ final String m = ("+++ " + msg + fnd);
+ logVerbose(m);
+ }
+
+ /** Returns the class objects for given (Java) user type names. */
+ protected Class[] getClasses(String[] userTypeName) {
+ final Class[] cls = new Class[userTypeName.length];
+ for (int i = userTypeName.length - 1; i >= 0; i--) {
+ cls[i] = getClass(userTypeName[i]);
+ }
+ return cls;
+ }
+
+ /** Returns the class object for a given (Java) user type name. */
+ protected Class getClass(String userTypeName) {
+ // use helper for retrieving class objects for primitive types
+ Class cls = TypeHelper.primitiveClass(userTypeName);
+ if (cls != null) {
+ return cls;
+ }
+
+ // load class
+ try {
+ final String r = TypeHelper.reflectionTypeName(userTypeName);
+ cls = classLoader.loadClass(r);
+ loading.add(userTypeName);
+ } catch (LinkageError err) {
+ handleNotLoading(err);
+ } catch (ClassNotFoundException ex) {
+ handleNotLoading(ex);
+ }
+ return cls;
+ }
+
+ /** Validates a field against a prescribed signature. */
+ protected void checkField(int mods, String type, String name,
+ String value) {
+ tested++;
+ type = TypeHelper.qualifiedUserTypeName(type);
+
+ // get field
+ final Field field;
+ try {
+ field = cls.getDeclaredField(name);
+ } catch (NoSuchFieldException ex) {
+ handleMissing(
+ "missing field: ",
+ Formatter.toString(mods, type, name, value));
+ return;
+ } catch (LinkageError err) {
+ handleNotLoading(err);
+ return;
+ }
+
+ // check modifiers
+ if (cls.isInterface()) {
+ // fields interfaces are implicitly public, static, and final
+ mods |= Modifier.PUBLIC;
+ mods |= Modifier.STATIC;
+ mods |= Modifier.FINAL;
+ }
+ if (mods != field.getModifiers()) {
+ handleMismatch(
+ "field declaration: non-matching modifiers;",
+ Formatter.toString(mods, type, name, null),
+ Formatter.toString(field, null));
+ }
+
+ // check type
+ if (!TypeHelper.isNameMatch(type, field.getType())) {
+ handleMismatch(
+ "field declaration: non-matching type;",
+ Formatter.toString(mods, type, name, null),
+ Formatter.toString(field, null));
+ }
+
+ // check field value if any
+ Object fieldValue = null;
+ if (value != null) {
+ // only support for public, static, and final fields
+ final int m = (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+ if ((mods & m) == 0) {
+ handleProblem("field declaration: ignoring field value "
+ + "definition in descriptor file;",
+ Formatter.toString(mods, type, name, value));
+ } else {
+ // only support for primitive types and String
+ final Object exp;
+ if (type.equals("byte")) {
+ exp = Byte.valueOf(value);
+ } else if (type.equals("short")) {
+ exp = Short.valueOf(value);
+ } else if (type.equals("integer")) {
+ exp = Integer.valueOf(value);
+ } else if (type.equals("long")) {
+ exp = Long.valueOf(value);
+ } else if (type.equals("float")) {
+ exp = Float.valueOf(value);
+ } else if (type.equals("double")) {
+ exp = Double.valueOf(value);
+ } else if (type.equals("char")) {
+ // cut off '\'' char at begin and end
+ exp = Character.valueOf(value.charAt(1));
+ } else if (type.equals("java.lang.String")) {
+ // cut off '\"' chars at begin and end
+ final String s = value.substring(1, value.length() - 1);
+ exp = String.valueOf(s);
+ } else {
+ exp = null;
+ }
+
+ // compare field's expected with found value
+ try {
+ fieldValue = field.get(null);
+ } catch (IllegalAccessException ex) {
+ handleProblem("field declaration: cannot access field "
+ + "value, exception: " + ex + ";",
+ Formatter.toString(mods, type, name, value));
+ }
+ if (exp != null && !exp.equals(fieldValue)) {
+ handleMismatch(
+ "field declaration: non-matching values;",
+ Formatter.toString(mods, type, name, exp.toString()),
+ Formatter.toString(field, fieldValue));
+ }
+ }
+ }
+
+ // field OK
+ members.remove(field);
+ handleMatch("has field: ", Formatter.toString(field, fieldValue));
+ }
+
+ /** Validates a constructor against a prescribed signature. */
+ protected void checkConstructor(int mods, String[] params,
+ String[] excepts) {
+ tested++;
+ params = TypeHelper.qualifiedUserTypeNames(params);
+ excepts = TypeHelper.qualifiedUserTypeNames(excepts);
+
+ // get parameter classes
+ final Class[] prms = getClasses(params);
+ if (prms == null) {
+ return;
+ }
+
+ // get constructor
+ final Constructor ctor;
+ try {
+ ctor = cls.getDeclaredConstructor(prms);
+ } catch (NoSuchMethodException ex) {
+ String name = cls.getName();
+ final int i = name.lastIndexOf('.');
+ name = (i < 0 ? name : name.substring(i));
+ handleMissing(
+ "missing constructor: ",
+ Formatter.toString(mods, null, name, params, excepts));
+ return;
+ } catch (LinkageError err) {
+ handleNotLoading(err);
+ return;
+ }
+
+ // check modifiers
+ if (mods != ctor.getModifiers()) {
+ handleMismatch(
+ "constructor declaration: non-matching modifiers;",
+ Formatter.toString(mods, null, cls.getName(), params, excepts),
+ Formatter.toString(ctor));
+ }
+
+ // check exceptions
+ if (!TypeHelper.isNameMatch(excepts, ctor.getExceptionTypes())) {
+ handleMismatch(
+ "method declaration: non-matching exceptions;",
+ Formatter.toString(mods, null, cls.getName(), params, excepts),
+ Formatter.toString(ctor));
+ }
+
+ // constructor OK
+ members.remove(ctor);
+ handleMatch("has constructor: ", Formatter.toString(ctor));
+ }
+
+ /** Validates a method against a prescribed signature. */
+ protected void checkMethod(int mods, String result, String name,
+ String[] params, String[] excepts) {
+ tested++;
+ params = TypeHelper.qualifiedUserTypeNames(params);
+ excepts = TypeHelper.qualifiedUserTypeNames(excepts);
+ result = TypeHelper.qualifiedUserTypeName(result);
+
+ // get parameter classes
+ final Class[] prms = getClasses(params);
+ if (prms == null) {
+ return;
+ }
+
+ // get method
+ final Method method;
+ try {
+ method = cls.getDeclaredMethod(name, prms);
+ } catch (NoSuchMethodException ex) {
+ handleMissing(
+ "missing method: ",
+ Formatter.toString(mods, result, name, params, excepts));
+ return;
+ } catch (LinkageError err) {
+ handleNotLoading(err);
+ return;
+ }
+
+ // check modifiers
+ if (cls.isInterface()) {
+ // methods in interfaces are implicitly public and abstract final
+ mods |= Modifier.PUBLIC;
+ mods |= Modifier.ABSTRACT;
+ }
+ if (mods != method.getModifiers()) {
+ handleMismatch(
+ "method declaration: non-matching modifiers;",
+ Formatter.toString(mods, result, name, params, excepts),
+ Formatter.toString(method));
+ }
+
+ // check return type
+ if (!TypeHelper.isNameMatch(result, method.getReturnType())) {
+ handleMismatch(
+ "method declaration: non-matching return type",
+ Formatter.toString(mods, result, name, params, excepts),
+ Formatter.toString(method));
+ }
+
+ // check exceptions
+ if (!TypeHelper.isNameMatch(excepts, method.getExceptionTypes())) {
+ handleMismatch(
+ "method declaration: non-matching exceptions;",
+ Formatter.toString(mods, result, name, params, excepts),
+ Formatter.toString(method));
+ }
+
+ // method OK
+ members.remove(method);
+ handleMatch("has method: ", Formatter.toString(method));
+ }
+
+ /** Validates a class declaration against a prescribed signature. */
+ protected void checkClass(int mods, String name,
+ String[] ext, String[] impl) {
+ logVerbose("");
+ logVerbose("testing " + Formatter.toString(mods, name, ext, impl));
+ tested++;
+ ext = TypeHelper.qualifiedUserTypeNames(ext);
+ impl = TypeHelper.qualifiedUserTypeNames(impl);
+
+ // get and assign currently processed class
+ cls = getClass(name);
+ if (cls == null) {
+ return; // can't load class
+ }
+
+ // collect all declared members of current class
+ members.clear();
+ try {
+ members.addAll(Arrays.asList(cls.getDeclaredFields()));
+ members.addAll(Arrays.asList(cls.getDeclaredConstructors()));
+ members.addAll(Arrays.asList(cls.getDeclaredMethods()));
+ } catch (LinkageError err) {
+ handleNotLoading(err);
+ }
+
+ // check modifiers
+ final boolean isInterface = ((mods & Modifier.INTERFACE) != 0);
+ if (isInterface) {
+ mods |= Modifier.ABSTRACT;
+ }
+ if (mods != cls.getModifiers()) {
+ handleMismatch(
+ "class declaration: non-matching modifiers;",
+ Formatter.toString(mods, name, ext, impl),
+ Formatter.toString(cls));
+ }
+
+ // check superclass and extended/implemented interfaces
+ final Class superclass = cls.getSuperclass();
+ final Class[] interfaces = cls.getInterfaces();
+ if (isInterface) {
+ //assert (impl.length == 0);
+ if (!TypeHelper.isNameMatch(ext, interfaces)) {
+ handleMismatch(
+ "interface declaration: non-matching interfaces;",
+ Formatter.toString(mods, name, ext, impl),
+ Formatter.toString(cls));
+ }
+ } else {
+ //assert (ext.length <= 1);
+ final String s = (ext.length == 0 ? "java.lang.Object" : ext[0]);
+ if (!TypeHelper.isNameMatch(s, superclass)) {
+ handleMismatch(
+ "class declaration: non-matching superclass;",
+ Formatter.toString(mods, name, ext, impl),
+ Formatter.toString(cls));
+ }
+ if (!TypeHelper.isNameMatch(impl, interfaces)) {
+ handleMismatch(
+ "class declaration: non-matching interfaces;",
+ Formatter.toString(mods, name, ext, impl),
+ Formatter.toString(cls));
+ }
+ }
+
+ handleMatch("has class: ", Formatter.toString(cls));
+ }
+
+ /** Runs checks on a class after its members have been validated. */
+ protected void postCheckClass() {
+ if (cls == null) {
+ return; // nothing to do if class couldn't be loaded
+ }
+
+ // check for public non-standard members
+ for (Iterator i = members.iterator(); i.hasNext();) {
+ final Member m = (Member)i.next();
+ if ((m.getModifiers() & Modifier.PUBLIC) != 0) {
+ handleNonStandard("non-standard, public member;",
+ Formatter.toString(m));
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Parser for Signature Descriptor Files
+ // ----------------------------------------------------------------------
+
+ /**
+ * For parsing of signature descriptor files.
+ */
+ protected class Parser {
+ /** The current descriptor file being parsed. */
+ private File descriptorFile;
+
+ /** The line number reader for the current descriptor file. */
+ private LineNumberReader ir;
+
+ /** A look-ahead token to be read next. */
+ private String nextToken;
+
+ /** Returns an error message reporting an unextected end of file. */
+ protected String msgUnexpectedEOF() {
+ return ("unexpected end of file at line: " + ir.getLineNumber()
+ + ", file: " + descriptorFile.getPath());
+ }
+
+ /** Returns an error message reporting an unextected token. */
+ protected String msgUnexpectedToken(String t) {
+ return ("unexpected token: '" + t + "'"
+ + " in line: " + ir.getLineNumber()
+ + ", file: " + descriptorFile.getPath());
+ }
+
+ /** Retrieves the look-ahead token to be parsed next. */
+ protected String getLookAhead() {
+ final String t = nextToken;
+ nextToken = null;
+ return t;
+ }
+
+ /** Sets the look-ahead token to be parsed next. */
+ protected void setLookAhead(String t) {
+ //assert (nextToken == null);
+ nextToken = t;
+ }
+
+ /**
+ * Skips any "white space" and returns whether there are more
+ * characters to be parsed.
+ */
+ protected boolean skip() throws IOException {
+ char c;
+ do {
+ ir.mark(1);
+ int i;
+ if ((i = ir.read()) < 0) {
+ return false;
+ }
+ c = (char)i;
+ } while (Character.isWhitespace(c));
+ ir.reset();
+ return true;
+ }
+
+ /**
+ * Scans for an (unqualified) identifier.
+ * @return <code>null</code> if the next token is not an identifier
+ */
+ protected String scanIdentifier()
+ throws IOException, ParseException {
+ // parse stored token if any
+ String t;
+ if ((t = getLookAhead()) != null) {
+ if (!Character.isJavaIdentifierStart(t.charAt(0))) {
+ setLookAhead(t); // not an identifier
+ return null;
+ }
+ return t;
+ }
+
+ // parse first char
+ if (!skip()) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ ir.mark(1);
+ char c = (char)ir.read();
+ if (!Character.isJavaIdentifierStart(c)) {
+ ir.reset(); // not start of an identifier
+ return null;
+ }
+
+ // parse remaining chars
+ final StringBuffer sb = new StringBuffer();
+ do {
+ sb.append(c);
+ int i;
+ ir.mark(1);
+ if ((i = ir.read()) < 0)
+ break;
+ c = (char)i;
+ } while (Character.isJavaIdentifierPart(c));
+ ir.reset(); // not part of an identifier
+ return sb.toString();
+ }
+
+ /**
+ * Scans for a number literal.
+ * @return <code>null</code> if the next token is not a number
+ */
+ protected String scanNumberLiteral()
+ throws IOException, ParseException {
+ // parse stored token if any
+ String t;
+ if ((t = getLookAhead()) != null) {
+ if (!(t.charAt(0) == '-' || Character.isDigit(t.charAt(0)))) {
+ setLookAhead(t); // not a number literal
+ return null;
+ }
+ return t;
+ }
+
+ // parse first char
+ if (!skip()) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ ir.mark(1);
+ char c = (char)ir.read();
+ if (Character.isDigit(c)) {
+ } else if (c == '-') {
+ skip();
+ } else {
+ ir.reset(); // not start of a number
+ return null;
+ }
+
+ // parse remaining chars
+ final StringBuffer sb = new StringBuffer();
+ do {
+ sb.append(c);
+ int i;
+ ir.mark(1);
+ if ((i = ir.read()) < 0)
+ break;
+ c = (char)i;
+ } while (Character.isLetterOrDigit(c) || c == '-' || c == '.');
+ ir.reset(); // not part of a number
+ return sb.toString();
+ }
+
+ /**
+ * Scans for a character literal.
+ * @return <code>null</code> if the next token is not a character
+ */
+ protected String scanCharacterLiteral()
+ throws IOException, ParseException {
+ // parse stored token if any
+ String t;
+ if ((t = getLookAhead()) != null) {
+ if (t.charAt(0) != '\'') {
+ setLookAhead(t); // not a char literal
+ return null;
+ }
+ return t;
+ }
+
+ // parse first char
+ if (!skip()) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ ir.mark(1);
+ char c = (char)ir.read();
+ if (c != '\'') {
+ ir.reset(); // not start of a char literal
+ return null;
+ }
+
+ // parse remaining two chars
+ final StringBuffer sb = new StringBuffer();
+ for (int j = 0; j < 2; j++) {
+ sb.append(c);
+ int i;
+ if ((i = ir.read()) < 0) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ c = (char)i;
+ }
+ if (c != '\'') {
+ throw new ParseException(msgUnexpectedToken(String.valueOf(c)),
+ 0);
+ }
+ sb.append(c); // keep '\'' part of a char literal
+ return sb.toString();
+ }
+
+ /**
+ * Scans for a string literal.
+ * @return <code>null</code> if the next token is not a string
+ */
+ protected String scanStringLiteral()
+ throws IOException, ParseException {
+ // parse stored token if any
+ String t;
+ if ((t = getLookAhead()) != null) {
+ if (t.charAt(0) != '\"') {
+ setLookAhead(t); // not a string literal
+ return null;
+ }
+ return t;
+ }
+
+ // parse first char
+ if (!skip()) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ ir.mark(1);
+ char c = (char)ir.read();
+ if (c != '\"') {
+ ir.reset(); // not start of a string literal
+ return null;
+ }
+
+ // parse remaining chars
+ final StringBuffer sb = new StringBuffer();
+ do {
+ sb.append(c);
+ int i;
+ if ((i = ir.read()) < 0) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ c = (char)i;
+ } while (c != '\"'); // not supported: nested '\"' char sequences
+ sb.append(c); // keep '\"' part of a string literal
+ return sb.toString();
+ }
+
+ /**
+ * Returns the next token to be parsed.
+ * @return never <code>null</code>
+ */
+ protected String parseToken()
+ throws IOException, ParseException {
+ String t;
+ if ((t = getLookAhead()) != null) {
+ } else if ((t = scanIdentifier()) != null) {
+ } else if ((t = scanNumberLiteral()) != null) {
+ } else if ((t = scanStringLiteral()) != null) {
+ } else if ((t = scanCharacterLiteral()) != null) {
+ } else {
+ setLookAhead(t); // not an identifier, number, or string
+ // next non-white char
+ if (!skip()) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ t = String.valueOf((char)ir.read());
+ }
+ //log.println("parseToken() : '" + t + "'");
+ return t;
+ }
+
+ /**
+ * Parses the next token and validates it against an expected one.
+ * @return never <code>null</code>
+ */
+ protected String demandToken(String token)
+ throws IOException, ParseException {
+ final String t = parseToken();
+ if (!t.equals(token)) {
+ throw new ParseException(msgUnexpectedToken(t), 0);
+ }
+ return t;
+ }
+
+ /**
+ * Parses a literal.
+ * @return <code>null</code> if the next token is not a literal
+ */
+ protected String parseLiteral()
+ throws IOException, ParseException {
+ String t;
+ if ((t = scanNumberLiteral()) != null) {
+ } else if ((t = scanStringLiteral()) != null) {
+ } else if ((t = scanCharacterLiteral()) != null) {
+ }
+ //log.println("parseLiteral() : '" + t + "'");
+ return t;
+ }
+
+ /**
+ * Parses the next token and validates that it is a literal.
+ * @return never <code>null</code>
+ */
+ protected String demandLiteral()
+ throws IOException, ParseException {
+ final String l = parseLiteral();
+ if (l == null) {
+ throw new ParseException(msgUnexpectedToken(parseToken()), 0);
+ }
+ return l;
+ }
+
+ /**
+ * Parses any available Java modifiers.
+ * @return an int value with the parsed modifiers' bit set
+ */
+ protected int parseModifiers()
+ throws IOException, ParseException {
+ int m = 0;
+ while (true) {
+ // parse known modifiers
+ final String t = parseToken();
+ if (t.equals("abstract")) m |= Modifier.ABSTRACT;
+ else if (t.equals("final")) m |= Modifier.FINAL;
+ else if (t.equals("interface")) m |= Modifier.INTERFACE;
+ else if (t.equals("native")) m |= Modifier.NATIVE;
+ else if (t.equals("private")) m |= Modifier.PRIVATE;
+ else if (t.equals("protected")) m |= Modifier.PROTECTED;
+ else if (t.equals("public")) m |= Modifier.PUBLIC;
+ else if (t.equals("static")) m |= Modifier.STATIC;
+ else if (t.equals("strictfp")) m |= Modifier.STRICT;
+ else if (t.equals("synchronized")) m |= Modifier.SYNCHRONIZED;
+ else if (t.equals("transient")) m |= Modifier.TRANSIENT;
+ else if (t.equals("volatile")) m |= Modifier.VOLATILE;
+ else {
+ setLookAhead(t); // not a modifier
+ break;
+ }
+ }
+ //log.println("parseModifiers() : '" + Modifier.toString(m) + "'");
+ return m;
+ }
+
+ /**
+ * Parses a (qualified) identifier.
+ * @return <code>null</code> if the next token is not an identifier
+ */
+ protected String parseIdentifier()
+ throws IOException, ParseException {
+ String t = scanIdentifier();
+ if (t != null) {
+ // parse dot-connected identifiers
+ final StringBuffer id = new StringBuffer(t);
+ String tt = parseToken();
+ while (tt.equals(".")) {
+ id.append(".");
+ tt = parseIdentifier();
+ if (tt == null) {
+ throw new ParseException(msgUnexpectedToken(tt), 0);
+ }
+ id.append(tt);
+ tt = parseToken();
+ }
+ setLookAhead(tt); // not a dot token
+ t = id.toString();
+ }
+ //log.println("parseIdentifier() : '" + t + "'");
+ return t;
+ }
+
+ /**
+ * Parses the next token(s) and validates that it is an identifier.
+ * @return never <code>null</code>
+ */
+ protected String demandIdentifier()
+ throws IOException, ParseException {
+ final String id = parseIdentifier();
+ if (id == null) {
+ throw new ParseException(msgUnexpectedToken(parseToken()), 0);
+ }
+ return id;
+ }
+
+ /**
+ * Parses a comma-separated list of identifiers.
+ * @return never <code>null</code>
+ */
+ protected String[] demandIdentifierList()
+ throws IOException, ParseException {
+ final ArrayList ids = new ArrayList();
+ ids.add(demandIdentifier());
+ String t;
+ while ((t = parseToken()).equals(",")) {
+ ids.add(demandIdentifier());
+ }
+ setLookAhead(t); // not an identifier
+ return (String[])ids.toArray(new String[ids.size()]);
+ }
+
+ /**
+ * Parses a type expression.
+ * @return <code>null</code> if the next token is not a type
+ */
+ protected String parseType()
+ throws IOException, ParseException {
+ String t = parseIdentifier();
+ if (t != null) {
+ // parse array dimensions
+ final StringBuffer type = new StringBuffer(t);
+ while ((t = parseToken()).equals("[")) {
+ demandToken("]");
+ type.append("[]");
+ }
+ setLookAhead(t); // not an open bracket token
+ t = type.toString();
+ }
+ //log.println("parseType() : '" + t + "'");
+ return t;
+ }
+
+ /**
+ * Parses the next token and validates that it is a type expression.
+ * @return never <code>null</code>
+ */
+ protected String demandType()
+ throws IOException, ParseException {
+ final String id = parseType();
+ if (id == null) {
+ throw new ParseException(msgUnexpectedToken(parseToken()), 0);
+ }
+ return id;
+ }
+
+ /**
+ * Parses a comma-separated parameter list.
+ * @return never <code>null</code>
+ */
+ protected String[] parseParameterList()
+ throws IOException, ParseException {
+ final ArrayList types = new ArrayList();
+ String t = parseType();
+ if (t != null) {
+ types.add(t);
+ parseIdentifier(); // optional parameter name
+ while ((t = parseToken()).equals(",")) {
+ types.add(demandType());
+ parseIdentifier(); // optional parameter name
+ }
+ setLookAhead(t); // not a comma token
+ }
+ return (String[])types.toArray(new String[types.size()]);
+ }
+
+ /**
+ * Parses a class member declaration and provides the information
+ * to a field, constructor, or method handler.
+ * @return <code>null</code> if there's no member declaration
+ */
+ protected String parseMember()
+ throws IOException, ParseException {
+ // parse optional modifiers, type, and member name
+ final int mods = parseModifiers();
+ final String typeOrName = parseType();
+ if (typeOrName == null) {
+ if (mods != 0) {
+ throw new ParseException(msgUnexpectedEOF(), 0);
+ }
+ return null; // no member to parse
+ }
+ final String memberName = parseIdentifier(); // null if constructor
+
+ // parse optional field value or parameter+exception list
+ final String value;
+ final String[] params;
+ final String[] excepts;
+ {
+ final String tvp = parseToken();
+ if (tvp.equals(";")) {
+ value = null;
+ params = null;
+ excepts = null;
+ } else if (tvp.equals("=")) {
+ // parse field value
+ value = demandLiteral();
+ demandToken(";");
+ params = null;
+ excepts = null;
+ } else if (tvp.equals("(")) {
+ // parse optional parameter and exception list
+ params = parseParameterList();
+ demandToken(")");
+ final String tt = parseToken();
+ if (tt.equals("throws")) {
+ excepts = demandIdentifierList();
+ demandToken(";");
+ } else if (tt.equals(";")) {
+ excepts = new String[]{};
+ } else {
+ throw new ParseException(msgUnexpectedToken(tt), 0);
+ }
+ value = null;
+ } else {
+ throw new ParseException(msgUnexpectedToken(tvp), 0);
+ }
+ }
+
+ // verify field, constructor, or method
+ String name = memberName;
+ if (params == null) {
+ checkField(mods, typeOrName, memberName, value);
+ } else {
+ if (memberName == null) {
+ name = typeOrName;
+ checkConstructor(mods, params, excepts);
+ } else {
+ checkMethod(mods, typeOrName, memberName, params, excepts);
+ }
+ }
+
+ //log.println("parseMember() : " + name);
+ return name;
+ }
+
+ /**
+ * Parses a class definition and provides the information
+ * to a handler.
+ * @return <code>null</code> if there's no class definition
+ */
+ protected String parseClass()
+ throws IOException, ParseException {
+ // parse optional modifiers, class token, and class name
+ if (!skip()) {
+ return null; // eof, no class to parse
+ }
+ final int mods = parseModifiers();
+ final String tc = parseToken();
+ if (!tc.equals("class")) { // token 'interface' parsed as modifier
+ setLookAhead(tc);
+ }
+ final String name = demandIdentifier();
+
+ // parse optional extends and implements clauses
+ final String[] ext;
+ final String[] impl;
+ {
+ String tei = parseToken();
+ if (tei.equals("extends")) {
+ ext = demandIdentifierList();
+ tei = parseToken();
+ } else {
+ ext = new String[]{};
+ }
+ if (((mods & Modifier.INTERFACE) == 0)
+ && tei.equals("implements")) {
+ impl = demandIdentifierList();
+ tei = parseToken();
+ } else {
+ impl = new String[]{};
+ }
+ if (!tei.equals("{")) {
+ throw new ParseException(msgUnexpectedToken(tei), 0);
+ }
+ }
+
+ // verify class header
+ checkClass(mods, name, ext, impl);
+
+ // process members
+ while (parseMember() != null);
+ demandToken("}");
+
+ // verify class
+ postCheckClass();
+
+ //log.println("parseClass() : " + name);
+ return name;
+ }
+
+ /**
+ * Parses a list of signature descriptor files and processes
+ * the class definitions.
+ * @param descrFileNames list of signature descriptor file names
+ */
+ public void parse(List descrFileNames)
+ throws IOException, ParseException {
+ for (Iterator i = descrFileNames.iterator(); i.hasNext();) {
+ final String fileName = (String)i.next();
+ logInfo("");
+ logInfo("parsing descriptor file: " + fileName);
+ try {
+ descriptorFile = new File(fileName);
+ ir = new LineNumberReader(new FileReader(descriptorFile));
+ ir.setLineNumber(1);
+ setLookAhead(null);
+ while (parseClass() != null);
+ } finally {
+ descriptorFile = null;
+ if (ir != null) {
+ ir.close();
+ }
+ setLookAhead(null);
+ }
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Stand-Alone Command-Line Interface
+ // ----------------------------------------------------------------------
+
+ /** A writer for standard output. */
+ static protected PrintWriter out = new PrintWriter(System.out, true);
+
+ /** Command line arguments */
+ static public final List descrFileNames = new ArrayList();
+
+ /** Command line option 'quiet'.*/
+ static private boolean optionQuiet;
+
+ /** Command line option 'verbose'.*/
+ static private boolean optionVerbose;
+
+ /** Prints the CLI usage. */
+ static public void printUsage() {
+ out.println();
+ out.println("usage: SignatureVerifier [options] arguments");
+ out.println("options:");
+ out.println(" [-h|--help print usage and exit]");
+ out.println(" [-q|--quiet only print error messages]");
+ out.println(" [-v|--verbose print verbose messages]");
+ out.println("arguments:");
+ out.println(" <signature descriptor file>...");
+ out.println();
+ }
+
+ /** Parses command line arguments. */
+ static public int parseArgs(String args[]) {
+ out.println("parse main() arguments");
+
+ // parse this class' options and arguments
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] == null) {
+ continue;
+ }
+ if (args[i].equalsIgnoreCase("-h")
+ || args[i].equalsIgnoreCase("--help")) {
+ return -1;
+ }
+ if (args[i].equalsIgnoreCase("-v")
+ || args[i].equalsIgnoreCase("--verbose")) {
+ optionVerbose = true;
+ continue;
+ }
+ if (args[i].equalsIgnoreCase("-q")
+ || args[i].equalsIgnoreCase("--quiet")) {
+ optionQuiet = true;
+ continue;
+ }
+ if (args[i].startsWith("-")) {
+ out.println("Usage Error: unknown option " + args[i]);
+ return -1;
+ }
+ // collect argument
+ descrFileNames.add(args[i]);
+ }
+
+ // print args
+ if (false) {
+ out.println("descrFileNames = {");
+ for (Iterator i = descrFileNames.iterator(); i.hasNext();) {
+ out.println(" " + i.next());
+ }
+ out.println(" }");
+ out.println("optionQuiet = " + optionQuiet);
+ out.println("optionVerbose = " + optionVerbose);
+ }
+
+ // check args
+ if (descrFileNames.isEmpty()) {
+ out.println("Usage Error: Missing argument "
+ + "<signature descriptor file>...");
+ return -1;
+ }
+ return 0;
+ }
+
+ /** Runs the signature test and exits with a status code. */
+ static public void main(String[] args) {
+ out.println("run SignatureVerifier ...");
+ if (parseArgs(args) != 0) {
+ printUsage();
+ out.println("abort.");
+ System.exit(-1);
+ }
+
+ int status = 0;
+ try {
+ final SignatureVerifier s
+ = new SignatureVerifier(out, optionQuiet, optionVerbose);
+ status = s.test(descrFileNames);
+ } catch (IOException ex) {
+ out.println("ERROR: exception caught: " + ex);
+ //ex.printStackTrace();
+ out.println("abort.");
+ System.exit(-2);
+ } catch (ParseException ex) {
+ out.println("ERROR: exception caught: " + ex);
+ //ex.printStackTrace();
+ out.println("abort.");
+ System.exit(-3);
+ }
+
+ out.println();
+ out.println("done.");
+ System.exit(status);
+ }
+}
Added: db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/TypeHelper.java
URL: http://svn.apache.org/viewcvs/db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/TypeHelper.java?rev=386448&view=auto
==============================================================================
--- db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/TypeHelper.java (added)
+++ db/jdo/trunk/tck20/src/java/org/apache/jdo/tck/util/signature/TypeHelper.java Thu Mar 16 12:06:59 2006
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jdo.tck.util.signature;
+
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Map;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.HashMap;
+
+/**
+ * A helper class for translating between Java user type names and
+ * reflection type names.
+ */
+public class TypeHelper {
+ /** Error message for format errors with a user type name. */
+ static private final String MSG_ILLEGAL_USR_TYPE
+ = "illegal user type name: ";
+
+ /** Error message for format errors with a reflection type name. */
+ static private final String MSG_ILLEGAL_RFL_TYPE
+ = "illegal reflection type name: ";
+
+ /** Throws an IllegalArgumentException if a given condition is violated. */
+ static private void check(boolean cond, String msg) {
+ if (!cond) throw new IllegalArgumentException(msg);
+ }
+
+ /** Maps primitive reflection type names to (Java) user names. */
+ static private final Map userTypeNames = new HashMap();
+
+ /** Maps primitive (Java) user type names to reflection names. */
+ static private final Map reflectionTypeNames = new HashMap();
+
+ /** Maps primitive type names to class objects. */
+ static private final Map primitiveClasses = new HashMap();
+
+ // initializes the type maps
+ static {
+ userTypeNames.put("B", "byte");
+ userTypeNames.put("C", "char");
+ userTypeNames.put("D", "double");
+ userTypeNames.put("F", "float");
+ userTypeNames.put("I", "int");
+ userTypeNames.put("J", "long");
+ userTypeNames.put("S", "short");
+ userTypeNames.put("Z", "boolean");
+ userTypeNames.put("V", "void");
+
+ for (Iterator i = userTypeNames.entrySet().iterator();
+ i.hasNext();) {
+ final Map.Entry e = (Map.Entry)i.next();
+ reflectionTypeNames.put(e.getValue(), e.getKey());
+ }
+
+ primitiveClasses.put("byte", byte.class);
+ primitiveClasses.put("char", char.class);
+ primitiveClasses.put("double", double.class);
+ primitiveClasses.put("float", float.class);
+ primitiveClasses.put("int", int.class);
+ primitiveClasses.put("long", long.class);
+ primitiveClasses.put("short", short.class);
+ primitiveClasses.put("boolean", boolean.class);
+ primitiveClasses.put("void", void.class);
+ }
+
+ /** Returns the (Java) user name for a reflection type name. */
+ static public String userTypeName(String name) {
+ check(name != null, MSG_ILLEGAL_RFL_TYPE + name);
+
+ // count array dimensions from start
+ final int n = name.length();
+ check(n > 0, MSG_ILLEGAL_RFL_TYPE + name);
+ int i = 0;
+ final StringBuffer sb = new StringBuffer();
+ while (name.charAt(i) == '[') {
+ sb.append("[]");
+ i++;
+ check(i < n, MSG_ILLEGAL_RFL_TYPE + name);
+ }
+
+ // no translation of primitive type names if not an array type
+ if (i == 0) {
+ return name;
+ }
+
+ // translate and recompose name
+ final String s;
+ if (name.charAt(i) == 'L') {
+ check(name.endsWith(";"), MSG_ILLEGAL_RFL_TYPE + name);
+ s = name.substring(i + 1, n - 1);
+ } else {
+ s = (String)userTypeNames.get(name.substring(i));
+ check(s != null, MSG_ILLEGAL_RFL_TYPE + name);
+ }
+ return (s + sb.toString());
+ }
+
+ /** Returns the (Java) user names for reflection type names. */
+ static public String[] userTypeNames(String[] names) {
+ final String[] u = new String[names.length];
+ for (int i = names.length - 1; i >= 0; i--) {
+ u[i] = userTypeName(names[i]);
+ }
+ return u;
+ }
+
+ /** Returns the reflection name for a (Java) user type name. */
+ static public String reflectionTypeName(String name) {
+ check(name != null, MSG_ILLEGAL_USR_TYPE + name);
+
+ // count array dimensions from end
+ final int n = name.length();
+ check(n > 0, MSG_ILLEGAL_USR_TYPE + name);
+ int i = n - 1;
+ final StringBuffer sb = new StringBuffer();
+ while (name.charAt(i) == ']') {
+ i--;
+ check(name.charAt(i) == '[', MSG_ILLEGAL_USR_TYPE + name);
+ sb.append("[");
+ i--;
+ check(i >= 0, MSG_ILLEGAL_USR_TYPE + name);
+ }
+
+ // no translation of primitive type names if not an array type
+ if (++i == n) {
+ return name;
+ }
+
+ // translate and recompose name
+ final String s = name.substring(0, i);
+ final String p = (String)reflectionTypeNames.get(s);
+ return sb.append(p != null ? p : "L" + s + ";").toString();
+ }
+
+ /** Returns the (Java) user names for reflection type names. */
+ static public String[] reflectionTypeNames(String[] names) {
+ final String[] r = new String[names.length];
+ for (int i = names.length - 1; i >= 0; i--) {
+ r[i] = reflectionTypeName(names[i]);
+ }
+ return r;
+ }
+
+ /**
+ * Returns the class object for a primitive type name, or
+ * <code>null</code> if the name does not denote a primitive type
+ * (class objects of primitive types cannot be loaded with reflection).
+ */
+ static public Class primitiveClass(String name) {
+ return (Class)primitiveClasses.get(name);
+ }
+
+ /** Tests if a name denotes a primitive type. */
+ static public boolean isPrimitive(String name) {
+ return primitiveClasses.containsKey(name);
+ }
+
+ /** Returns the component type name of a (Java) user type name. */
+ static public String componentUserTypeName(String name) {
+ check(name != null, MSG_ILLEGAL_USR_TYPE + name);
+ final int n = name.length();
+ check(n > 0, MSG_ILLEGAL_USR_TYPE + name);
+ final int i = name.indexOf('[');
+ if (i >= 0) {
+ check(i > 0, MSG_ILLEGAL_USR_TYPE + name);
+ name = name.substring(0, i);
+ }
+ return name;
+ }
+
+ /**
+ * Returns the <code>java.lang.</code>-qualified name for a given
+ * unqualified (Java) user type name.
+ */
+ static public String qualifiedUserTypeName(String name) {
+ final String c = componentUserTypeName(name);
+ return ((isPrimitive(c) || c.indexOf('.') >= 0)
+ ? name : "java.lang." + name);
+ }
+
+ /**
+ * Returns the <code>java.lang.</code>-qualified names for given
+ * unqualified (Java) user type names.
+ */
+ static public String[] qualifiedUserTypeNames(String[] names) {
+ final String[] q = new String[names.length];
+ for (int i = names.length - 1; i >= 0; i--) {
+ q[i] = qualifiedUserTypeName(names[i]);
+ }
+ return q;
+ }
+
+ /**
+ * Compares a type name with a class objects for equality in the name.
+ */
+ static public boolean isNameMatch(String userTypeName, Class cls) {
+ final String c = (cls == null ? null : userTypeName(cls.getName()));
+ return (userTypeName == null ? (c == null) : userTypeName.equals(c));
+ }
+
+ /**
+ * Compares an array of type names with an array of class objects
+ * for set-equality in the names (i.e., ignoring order).
+ */
+ static public boolean isNameMatch(String[] userTypeName, Class[] cls) {
+ final Set s = new HashSet(Arrays.asList(userTypeName));
+ for (int i = cls.length - 1; i >= 0; i--) {
+ if (!s.remove(userTypeName(cls[i].getName()))) {
+ return false;
+ }
+ }
+ return s.isEmpty();
+ }
+}