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();
+    }
+}