/*
* Copyright (c) 2004 David Flanagan. All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book,
* please visit http://www.davidflanagan.com/javaexamples3.
*/
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* This class is a standalone program that reads a list of classes and builds a
* database of packages, classes, and class fields and methods.
*/
public class MakeAPIDB {
public static void main(String args[]) {
Connection c = null; // The connection to the database
try {
// Read the classes to index from a file specified by args[0]
ArrayList classnames = new ArrayList();
BufferedReader in = new BufferedReader(new FileReader(args[0]));
String name;
while ((name = in.readLine()) != null)
classnames.add(name);
// Now determine the values needed to set up the database
// connection The program attempts to read a property file named
// "APIDB.props", or optionally specified by args[1]. This
// property file (if any) may contain "driver", "database", "user",
// and "password" properties that specify the necessary values for
// connecting to the db. If the properties file does not exist, or
// does not contain the named properties, defaults will be used.
Properties p = new Properties(); // Empty properties
try {
p.load(new FileInputStream(args[1])); // Try to load properties
} catch (Exception e1) {
try {
p.load(new FileInputStream("APIDB.props"));
} catch (Exception e2) {
}
}
// Read values from Properties file
String driver = p.getProperty("driver");
String database = p.getProperty("database");
String user = p.getProperty("user", "");
String password = p.getProperty("password", "");
// The driver and database properties are mandatory
if (driver == null)
throw new IllegalArgumentException("No driver specified!");
if (database == null)
throw new IllegalArgumentException("No database specified!");
// Load the driver. It registers itself with DriverManager.
Class.forName(driver);
// And set up a connection to the specified database
c = DriverManager.getConnection(database, user, password);
// Create three new tables for our data
// The package table contains a package id and a package name.
// The class table contains a class id, a package id, and a name.
// The member table contains a class id, a member name, and an bit
// that indicates whether the class member is a field or a method.
Statement s = c.createStatement();
s.executeUpdate("CREATE TABLE package " + "(id INT, name VARCHAR(80))");
s.executeUpdate("CREATE TABLE class " + "(id INT, packageId INT, name VARCHAR(48))");
s.executeUpdate("CREATE TABLE member " + "(classId INT, name VARCHAR(48), isField BIT)");
// Prepare some statements that will be used to insert records into
// these three tables.
insertpackage = c.prepareStatement("INSERT INTO package VALUES(?,?)");
insertclass = c.prepareStatement("INSERT INTO class VALUES(?,?,?)");
insertmember = c.prepareStatement("INSERT INTO member VALUES(?,?,?)");
// Now loop through the list of classes and use reflection
// to store them all in the tables
int numclasses = classnames.size();
for (int i = 0; i < numclasses; i++) {
try {
storeClass((String) classnames.get(i));
} catch (ClassNotFoundException e) {
System.out.println("WARNING: class not found: " + classnames.get(i) + "; SKIPPING");
}
}
} catch (Exception e) {
System.err.println(e);
if (e instanceof SQLException)
System.err.println("SQLState: " + ((SQLException) e).getSQLState());
System.err.println("Usage: java MakeAPIDB " + " ");
}
// When we're done, close the connection to the database
finally {
try {
c.close();
} catch (Exception e) {
}
}
}
/**
* This hash table records the mapping between package names and package id.
* This is the only one we need to store temporarily. The others are stored in
* the db and don't have to be looked up by this program
*/
static Map package_to_id = new HashMap();
// Counters for the package and class identifier columns
static int packageId = 0, classId = 0;
// Some prepared SQL statements for use in inserting
// new values into the tables. Initialized in main() above.
static PreparedStatement insertpackage, insertclass, insertmember;
/**
* Given a fully-qualified classname, this method stores the package name in
* the package table (if it is not already there), stores the class name in
* the class table, and then uses the Java Reflection API to look up all
* methods and fields of the class, and stores those in the member table.
*/
public static void storeClass(String name) throws SQLException, ClassNotFoundException {
String packagename, classname;
// Dynamically load the class.
Class c = Class.forName(name);
// Display output so the user knows that the program is progressing
System.out.println("Storing data for: " + name);
// Figure out the packagename and the classname
int pos = name.lastIndexOf('.');
if (pos == -1) {
packagename = "";
classname = name;
} else {
packagename = name.substring(0, pos);
classname = name.substring(pos + 1);
}
// Figure out what the package id is. If there is one, then this
// package has already been stored in the database. Otherwise, assign
// an id, and store it and the packagename in the db.
Integer pid;
pid = (Integer) package_to_id.get(packagename); // Check hashtable
if (pid == null) {
pid = new Integer(++packageId); // Assign an id
package_to_id.put(packagename, pid); // Remember it
insertpackage.setInt(1, packageId); // Set statement args
insertpackage.setString(2, packagename);
insertpackage.executeUpdate(); // Insert into package db
}
// Now, store the classname in the class table of the database.
// This record includes the package id, so that the class is linked to
// the package that contains it. To store the class, we set arguments
// to the PreparedStatement, then execute that statement
insertclass.setInt(1, ++classId); // Set class identifier
insertclass.setInt(2, pid.intValue()); // Set package identifier
insertclass.setString(3, classname); // Set class name
insertclass.executeUpdate(); // Insert the class record
// Now, get a list of all non-private methods of the class, and
// insert those into the "members" table of the database. Each
// record includes the class id of the containing class, and also
// a value that indicates that these are methods, not fields.
Method[] methods = c.getDeclaredMethods(); // Get a list of methods
for (int i = 0; i < methods.length; i++) { // For all non-private
if (Modifier.isPrivate(methods[i].getModifiers()))
continue;
insertmember.setInt(1, classId); // Set the class id
insertmember.setString(2, methods[i].getName()); // Set method name
insertmember.setBoolean(3, false); // It is not a field
insertmember.executeUpdate(); // Insert into db
}
// Do the same thing for the non-private fields of the class
Field[] fields = c.getDeclaredFields(); // Get a list of fields
for (int i = 0; i < fields.length; i++) { // For each non-private
if (Modifier.isPrivate(fields[i].getModifiers()))
continue;
insertmember.setInt(1, classId); // Set the class id
insertmember.setString(2, fields[i].getName()); // Set field name
insertmember.setBoolean(3, true); // It is a field
insertmember.executeUpdate(); // Insert the record
}
}
}