001: /*
002: * Copyright 2004 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: Enhancer.java,v 1.9 2004/01/25 22:30:58 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.enhance;
012:
013: import com.triactive.jdo.model.Types;
014: import com.triactive.jdo.util.XMLHelper;
015: import java.io.File;
016: import java.io.IOException;
017: import java.io.InputStream;
018: import java.io.OutputStream;
019: import java.io.FileOutputStream;
020: import java.io.PrintWriter;
021: import java.util.Arrays;
022: import java.util.ArrayList;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028: import javax.jdo.spi.PersistenceCapable;
029: import javax.xml.parsers.DocumentBuilder;
030: import javax.xml.parsers.ParserConfigurationException;
031: import org.w3c.dom.Element;
032: import org.w3c.dom.NodeList;
033: import org.xml.sax.SAXException;
034:
035: /**
036: * Abstract class for JDO bytecode enhancement utility. The expected
037: * command-line syntax is:
038: * <pre><blockquote>
039: * java com.triactive.jdo.enhance.Enhancer JDO-metadata-filename ...
040: * </blockquote></pre>
041: *
042: * Every class listed in the given metadata file(s) should be made persistence
043: * capable via bytecode enhancement.
044: *
045: * This class must be subclassed with an implementation that uses a working
046: * bytecode enhancer. The key method that needs to be overridden and implemented
047: * is callExternalEnhancer(String[]).
048: *
049: *
050: * @version $Revision: 1.9 $
051: */
052:
053: public abstract class Enhancer {
054:
055: /**
056: * Protected constructor to prevent outside instantiation.
057: */
058: protected Enhancer() {
059: }
060:
061: protected int enhance(List classNames) throws Exception {
062: /* Remove from the list any that are already enhanced. */
063: ArrayList l = new ArrayList(classNames);
064: Iterator i = l.iterator();
065:
066: while (i.hasNext()) {
067: if (isEnhanced((String) i.next()))
068: i.remove();
069: }
070:
071: if (l.isEmpty())
072: return 0;
073: else
074: return callExternalEnhancer((String[]) l
075: .toArray(new String[l.size()]));
076: }
077:
078: /**
079: * Returns true if the named class has already been enhanced. This method
080: * returns false if the enhancement status is unknown because the class
081: * cannot be loaded.
082: *
083: * @param className The name of the class to test.
084: *
085: * @return <tt>true</tt> if the class is enhanced,
086: * <tt>false</tt> otherwise.
087: */
088:
089: private boolean isEnhanced(String className) {
090: Class c;
091:
092: try {
093: c = Class.forName(className);
094: } catch (Throwable t) {
095: /*
096: * A class that fails to load is deemed to be unenhanced. This
097: * often happens when attempting to load an enhanced subclass whose
098: * superclass is unenhanced.
099: */
100: //t.printStackTrace();
101: return false;
102: }
103:
104: return Types.isEnhancedClass(c);
105: }
106:
107: /**
108: * Called by enhance() to enhance a list of classes.
109: * Subclasses implement this method to invoke the 3rd-party class enhancer.
110: *
111: * @param classNames The list of class names to be enhanced
112: */
113: protected abstract int callExternalEnhancer(String[] classNames)
114: throws Exception;
115:
116: /**
117: * Returns a list of all classes declared in the given metadata files,
118: * ordered such that all superclasses occur before their subclasses.
119: *
120: * @param metaDataFileNames The list of JDO metadata file names.
121: *
122: * @return An ordered list of class name strings.
123: *
124: * @exception IOException
125: * If any I/O error occurs.
126: * @exception ParserConfigurationXException
127: * If an XML document builder cannot be created.
128: * @exception SAXException
129: * If any parse error occurs.
130: */
131:
132: protected static List getOrderedClassNames(
133: String[] metaDataFileNames) throws IOException,
134: ParserConfigurationException, SAXException {
135: HashMap super classesByClass = new HashMap();
136: final DocumentBuilder db = XMLHelper.getDocumentBuilder();
137:
138: for (int i = 0; i < metaDataFileNames.length; ++i) {
139: Element docElement = db.parse(
140: new File(metaDataFileNames[i]))
141: .getDocumentElement();
142:
143: NodeList nodes = docElement.getElementsByTagName("class");
144:
145: for (int j = 0; j < nodes.getLength(); ++j) {
146: Element clsElement = (Element) nodes.item(j);
147: String packageName = ((Element) clsElement
148: .getParentNode()).getAttribute("name");
149: String className = packageName + '.'
150: + clsElement.getAttribute("name");
151: String super className = clsElement
152: .getAttribute("persistence-capable-superclass");
153:
154: if (super className.length() == 0)
155: super className = null;
156: else if (super className.indexOf('.') < 0)
157: super className = packageName + '.' + super className;
158:
159: super classesByClass.put(className, super className);
160: }
161: }
162:
163: int numClasses = super classesByClass.size();
164: ArrayList orderedNames = new ArrayList(numClasses);
165: ArrayList sortedNames = new ArrayList(numClasses);
166:
167: sortedNames.addAll(super classesByClass.keySet());
168: Collections.sort(sortedNames);
169:
170: Iterator i = sortedNames.iterator();
171:
172: while (i.hasNext())
173: addClass(orderedNames, (String) i.next(),
174: super classesByClass);
175:
176: return orderedNames;
177: }
178:
179: private static void addClass(List names, String className,
180: Map super classesByClass) {
181: String super className = (String) super classesByClass
182: .get(className);
183:
184: if (super className != null)
185: addClass(names, super className, super classesByClass);
186:
187: if (!names.contains(className))
188: names.add(className);
189: }
190:
191: /**
192: * Writes out the given list of classes to a file.
193: */
194: protected static void writeClassListFile(String filename,
195: List classNames) throws IOException {
196: PrintWriter pw = new PrintWriter(new FileOutputStream(new File(
197: filename)), true);
198:
199: try {
200: Iterator i = classNames.iterator();
201:
202: while (i.hasNext())
203: pw.println(i.next());
204: } finally {
205: pw.flush();
206: pw.close();
207: }
208: }
209: }
|