001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo.tools.enhancer;
012:
013: import com.versant.core.common.config.ConfigInfo;
014: import com.versant.core.common.config.ConfigParser;
015: import com.versant.core.jdo.tools.enhancer.info.ClassInfo;
016: import com.versant.core.jdo.tools.enhancer.info.FieldInfo;
017: import com.versant.core.jdo.tools.enhancer.utils.MetaDataToInfoMapper;
018: import com.versant.core.jdo.tools.enhancer.utils.SwapFieldHelper;
019: import com.versant.core.metadata.ClassMetaData;
020: import com.versant.core.metadata.ModelMetaData;
021: import com.versant.core.storagemanager.StorageManagerFactory;
022: import com.versant.core.storagemanager.StorageManagerFactoryBuilder;
023: import com.versant.core.util.BeanUtils;
024:
025: import java.io.File;
026: import java.io.IOException;
027: import java.util.*;
028:
029: /**
030: * The Enhancer class is used for enhancing .class files,
031: * to implement the Persistence Capable interfase.
032: * It can also be used from the command line.
033: */
034: public class Enhancer {
035:
036: private ClassEnhancer classEnhancer;
037: private FieldRefEnhancer fieldRefEnhancer;
038: private List classes = new ArrayList();
039: private HashMap classResourceMap = new HashMap(); // class name -> res name
040: private MetaDataToInfoMapper classInfoUtil;
041: private HashMap classMetaDataMap = new HashMap();
042: private ModelMetaData metaData = null;
043: protected Config cfg = new Config();
044:
045: private Set scopeFiles = new HashSet();
046: private Set queriesFiles = new HashSet();
047: private File outputDirectory;
048: private ClassLoader classLoader;
049: private String propertiesResourceName;
050: private File propertiesFile;
051: private boolean genHyper = false;
052: private boolean makeFieldsPrivate = false;
053: private boolean detached = true;
054: private File hyperdriveDir;
055: private File srcOutDir;
056: private boolean genSrc;
057:
058: public Enhancer() {
059: }
060:
061: public static class Config {
062:
063: public String outputDir;
064: public String inputDir;
065: public boolean h = false;
066: public String hyperDir;
067: public boolean genSrc = false;
068: public String srcOutputDir;
069: public boolean d = true;
070:
071: public static final String HELP_outputDir = "The output directory where the enhanced classes must be writen to "
072: + "(If not set then the classes will be written where they are found).";
073: public static final String HELP_hyperDir = "The output directory where the hyperdrive classes must be writen to.";
074: public static final String HELP_inputDir = "The Persistence Aware files. "
075: + "These are normal classes (i.e. NOT Persistence Capable classes) that "
076: + "reference the persistence capable classes fields directly.";
077: public static final String HELP_h = "generate hyperdrive classes at compile time.";
078: public static final String HELP_srcOutputDir = "The place where the hyperdrive src files must be written to.";
079: public static final String HELP_d = "enhanced for detach (true by default).";
080: public static final String HELP_genSrc = "The directory where the hyperdrive src files must be written to.";
081:
082: }
083:
084: public static void main(String[] args) {
085: Enhancer server = new Enhancer();
086: BeanUtils.CmdLineResult res = null;
087: try {
088: res = BeanUtils.processCmdLine(args, server.getClass()
089: .getClassLoader(), server.cfg, null, "Enhancer",
090: "The JDO class enhancer", true);
091: } catch (IllegalArgumentException e) {
092: // error message has already been printed
093: System.exit(1);
094: }
095: try {
096:
097: ArrayList scopeFiles = new ArrayList();
098: if (server.cfg.inputDir != null) {
099: StringTokenizer tokenizer = new StringTokenizer(
100: server.cfg.inputDir, ",", false);
101: while (tokenizer.hasMoreTokens()) {
102: scopeFiles.add(new File(tokenizer.nextToken()));
103: }
104: }
105:
106: if (server.cfg.srcOutputDir != null) {
107: server.cfg.genSrc = true;
108: }
109:
110: server.rumCommandLine(res.properties, server.cfg.outputDir,
111: scopeFiles, null, server.cfg.h, false,
112: server.cfg.d, server.cfg.hyperDir,
113: server.cfg.genSrc, server.cfg.srcOutputDir);
114: } catch (Exception e) {
115: e.printStackTrace();
116: System.exit(1);
117: }
118: }
119:
120: /**
121: * Gets the classloader
122: *
123: * @return
124: */
125: public ClassLoader getClassLoader() {
126: return classLoader;
127: }
128:
129: /**
130: * Sets a classloader for loading all the files.
131: *
132: * @param classLoader
133: */
134: public void setClassLoader(ClassLoader classLoader) {
135: this .classLoader = classLoader;
136: }
137:
138: /**
139: * Is the persistent capable classes enhanced for detached behavior.
140: *
141: * @return
142: */
143: public boolean isDetached() {
144: return detached;
145: }
146:
147: /**
148: * Must the persistent capable classes be enhanced for detached behavior.
149: *
150: * @param detached
151: */
152: public void setDetached(boolean detached) {
153: this .detached = detached;
154: }
155:
156: /**
157: * Is Hypedrive classes generated at enhancement time.
158: *
159: * @return
160: */
161: public boolean isGenHyper() {
162: return genHyper;
163: }
164:
165: /**
166: * Must Hypedrive classes be generated at enhancement time.
167: *
168: * @param genHyper
169: */
170: public void setGenHyper(boolean genHyper) {
171: this .genHyper = genHyper;
172: }
173:
174: /**
175: * Is Hypedrive src generated at enhancement time.
176: *
177: * @return
178: */
179: public boolean isGenSrc() {
180: return genSrc;
181: }
182:
183: /**
184: * Must Hypedrive src be generated at enhancement time.
185: *
186: * @param genSrc
187: */
188: public void setGenSrc(boolean genSrc) {
189: this .genSrc = genSrc;
190: }
191:
192: public File getSrcOutDir() {
193: return srcOutDir;
194: }
195:
196: public void setSrcOutDir(File srcOutDir) {
197: this .srcOutDir = srcOutDir;
198: }
199:
200: /**
201: * Is (public/protected/pakage) fields made private
202: * during enhancement.
203: *
204: * @return
205: */
206: public boolean isMakeFieldsPrivate() {
207: return makeFieldsPrivate;
208: }
209:
210: /**
211: * Must (public/protected/pakage) fields be made private
212: * during enhancement.
213: *
214: * @param makeFieldsPrivate
215: */
216: public void setMakeFieldsPrivate(boolean makeFieldsPrivate) {
217: this .makeFieldsPrivate = makeFieldsPrivate;
218: }
219:
220: /**
221: * Get the output directory where the enhanced classes are
222: * written to.
223: *
224: * @return
225: */
226: public File getOutputDir() {
227: return outputDirectory;
228: }
229:
230: /**
231: * Set the output directory where the enhanced classes are
232: * written to.
233: *
234: * @param outputDir
235: */
236: public void setOutputDir(File outputDir) {
237: this .outputDirectory = outputDir;
238: }
239:
240: /**
241: * Get the .jdogenie property file
242: *
243: * @return
244: */
245: public File getPropertiesFile() {
246: return propertiesFile;
247: }
248:
249: /**
250: * Sets the *.jdogenie property as a file.
251: *
252: * @param propertiesFile
253: */
254: public void setPropertiesFile(File propertiesFile) {
255: this .propertiesFile = propertiesFile;
256: }
257:
258: /**
259: * Gets the *.jdogenie property file resource name
260: *
261: * @return
262: */
263: public String getPropertiesResourceName() {
264: return propertiesResourceName;
265: }
266:
267: /**
268: * Sets the *.jdogenie property filename as a resource.
269: *
270: * @param propertiesResourceName
271: */
272: public void setPropertiesResourceName(String propertiesResourceName) {
273: this .propertiesResourceName = propertiesResourceName;
274: }
275:
276: // public Set getQueriesFiles() {
277: // return queriesFiles;
278: // }
279: //
280: // public void setQueriesFiles(Set queriesFiles) {
281: // this.queriesFiles = queriesFiles;
282: // }
283:
284: /**
285: * Persistant Aware file set as Strings
286: *
287: * @return
288: */
289: public Set getPCAwareFiles() {
290: return scopeFiles;
291: }
292:
293: /**
294: * Set of resource class file names as Strings i.e.
295: * "com/bla/model/Person.class" these classes are
296: * persistent aware file, i.e. these files are NOT
297: * Persistant Capable files, they are classes that
298: * use PC classes that have public fields.
299: *
300: * @param scopeFiles
301: */
302: public void setPCAwareFiles(Set scopeFiles) {
303: this .scopeFiles = scopeFiles;
304: }
305:
306: public void setHyperdriveDir(File hyperdriveDir) {
307: this .hyperdriveDir = hyperdriveDir;
308: }
309:
310: /**
311: * Enhance the files and write them to the output
312: * directory
313: *
314: * @throws Exception
315: */
316: public void enhance() throws Exception {
317: if (propertiesFile == null && propertiesResourceName == null) {
318: throw new Exception(
319: "Either the propertiesFile or propertiesResourceName is required.");
320: }
321:
322: setClassLoader(getPrivateClassLoader());
323: // if (outputDirectory == null) {
324: // throw new Exception("Output directory is required.");
325: // }
326:
327: if (propertiesFile != null) {
328: start(scopeFiles, queriesFiles, outputDirectory,
329: classLoader, propertiesFile, genHyper,
330: makeFieldsPrivate, detached, genSrc, srcOutDir);
331: } else {
332: start(scopeFiles, queriesFiles, outputDirectory,
333: classLoader, propertiesResourceName, genHyper,
334: makeFieldsPrivate, detached, genSrc, srcOutDir);
335: }
336:
337: }
338:
339: private void rumCommandLine(Properties props, String outputDir,
340: ArrayList scopeFiles, ArrayList queries, boolean genHyper,
341: boolean makeFieldsPrivate, boolean detached,
342: String hyperdriveDir, boolean genSrc, String hyperSrcDir)
343: throws Exception {
344: ClassLoader callingClassLoader = Enhancer.class
345: .getClassLoader();
346: GrepFile grepFile = null;
347:
348: try {
349: grepFile = new GrepFile(scopeFiles);
350: } catch (IOException e) {
351: e.printStackTrace(); //To change body of catch statement use Options | File Templates.
352: }
353:
354: if (outputDir != null) {
355: this .outputDirectory = new File(outputDir);
356: }
357:
358: if (hyperdriveDir != null) {
359: this .hyperdriveDir = new File(hyperdriveDir);
360: }
361:
362: if (hyperSrcDir != null) {
363: this .srcOutDir = new File(hyperSrcDir);
364: }
365: this .genSrc = genSrc;
366:
367: start(grepFile.getJdoFiles(), null, outputDirectory,
368: callingClassLoader, props, genHyper, makeFieldsPrivate,
369: detached, genSrc, srcOutDir);
370:
371: }
372:
373: private void start(Set scopeFiles, Set queriesFiles,
374: File outputDir, ClassLoader callingClassLoader,
375: File configFile, boolean genHyper,
376: boolean makeFieldsPrivate, boolean detached,
377: boolean genSrc, File srcOutDir) throws Exception {
378: this .genHyper = genHyper;
379: metaData = getMetaData(configFile, callingClassLoader);
380: startImp(scopeFiles, queriesFiles, outputDir,
381: callingClassLoader, genHyper, makeFieldsPrivate,
382: detached, genSrc, srcOutDir);
383: }
384:
385: private void start(Set scopeFiles, Set queriesFiles,
386: File outputDir, ClassLoader callingClassLoader,
387: Properties props, boolean genHyper,
388: boolean makeFieldsPrivate, boolean detached,
389: boolean genSrc, File srcOutDir) throws Exception {
390: this .genHyper = genHyper;
391: metaData = getMetaData(props, callingClassLoader);
392: startImp(scopeFiles, queriesFiles, outputDir,
393: callingClassLoader, genHyper, makeFieldsPrivate,
394: detached, genSrc, srcOutDir);
395: }
396:
397: private void start(Set scopeFiles, Set queriesFiles,
398: File outputDir, ClassLoader callingClassLoader,
399: String configFilename, boolean genHyper,
400: boolean makeFieldsPrivate, boolean detached,
401: boolean genSrc, File srcOutDir) throws Exception {
402: this .genHyper = genHyper;
403: metaData = getMetaData(configFilename, callingClassLoader);
404: startImp(scopeFiles, queriesFiles, outputDir,
405: callingClassLoader, genHyper, makeFieldsPrivate,
406: detached, genSrc, srcOutDir);
407:
408: }
409:
410: private void startImp(Set scopeFiles, Set queriesFiles,
411: File outputDir, ClassLoader callingClassLoader,
412: boolean genHyper, boolean makeFieldsPrivate,
413: boolean detached, boolean genSrc, File srcOutDir)
414: throws Exception {
415: classEnhancer = new ClassEnhancer(outputDir, callingClassLoader);
416: fieldRefEnhancer = new FieldRefEnhancer(outputDir,
417: callingClassLoader);
418: classInfoUtil = new MetaDataToInfoMapper(classResourceMap,
419: callingClassLoader);
420: for (int k = 0; k < metaData.classes.length; k++) {
421: ClassMetaData classMetaData = metaData.classes[k];
422: classMetaDataMap.put(classMetaData.qname, classMetaData);
423: classInfoUtil.setClassInfo(classMetaData.jdoClass,
424: classMetaData);
425: }
426:
427: classes = classInfoUtil.getClassInfoList();
428: long start = System.currentTimeMillis();
429: classEnhancer
430: .setGetAndSettersMap(getMapOfGetAndSetters(classes));
431: ListIterator classIter = classes.listIterator();
432: int classCount = 0;
433: while (classIter.hasNext()) {
434: ClassInfo classInfo = (ClassInfo) classIter.next();
435: fieldRefEnhancer.setPersistentCapable(classInfo
436: .getClassName());
437: // javaToJdoqlParser.setPersistentCapable(classInfo.getClassName());
438: ClassMetaData classMeta = (ClassMetaData) classMetaDataMap
439: .get(classInfo.getClassName());
440: if (classEnhancer.enhance(classInfo, classMeta,
441: makeFieldsPrivate, detached)) {
442: classCount++;
443: }
444: }
445:
446: long end = System.currentTimeMillis();
447: long time = end - start;
448: System.out.println("[Enhanced (" + classCount + ")] time = "
449: + time + " ms");
450:
451: start = System.currentTimeMillis();
452:
453: classCount = classes.size();
454: fieldRefEnhancer
455: .setFieldsToEnhanceMap(getNonPrivateMapOfGetAndSetters(classes));
456: fieldRefEnhancer.enhance(scopeFiles);
457:
458: end = System.currentTimeMillis();
459: time = end - start;
460: System.out.println("[Looked through " + scopeFiles.size()
461: + " class files and found "
462: + fieldRefEnhancer.getAwareNum()
463: + " Persistence Aware Classes] time = " + time + " ms");
464:
465: // start = System.currentTimeMillis();
466: // classCount = classes.size();
467: // JavaToJdoqlParser javaToJdoqlParser = new JavaToJdoqlParser(outputDir, callingClassLoader);
468: // javaToJdoqlParser.setMetaData(metaData);
469: // javaToJdoqlParser.enhance(queriesFiles);
470: // end = System.currentTimeMillis();
471: // time = end - start;
472: // System.out.println("[Looked through " + queriesFiles.size() + " class files and found " + javaToJdoqlParser.getQueryNum() + " JavaQuery Classes] time = " + time + " ms");
473: }
474:
475: private ClassLoader getPrivateClassLoader() {
476: ClassLoader classLoader = getClassLoader();
477: if (classLoader != null) {
478: return classLoader;
479: }
480: ClassLoader taskClassLoader = getClass().getClassLoader();
481: if (taskClassLoader == null) {
482: taskClassLoader = ClassLoader.getSystemClassLoader();
483: }
484: return taskClassLoader;
485: }
486:
487: ArrayList orderList = new ArrayList();
488:
489: private void getOrder(ClassMetaData meta) {
490: orderList.add(meta.qname);
491: ClassMetaData[] mySub = meta.pcSubclasses;
492: if (mySub == null) {
493: return;
494: } else {
495: for (int j = 0; j < mySub.length; j++) {
496: ClassMetaData data = mySub[j];
497: getOrder(data);
498: }
499: }
500: }
501:
502: ArrayList hierList = new ArrayList();
503:
504: private void getHier(ClassMetaData meta) {
505: hierList.add(meta.qname);
506: ClassMetaData[] mySub = meta.pcSubclasses;
507: if (mySub == null) {
508: return;
509: } else {
510: for (int j = 0; j < mySub.length; j++) {
511: ClassMetaData data = mySub[j];
512: getHier(data);
513: }
514: }
515: }
516:
517: private HashMap getMapOfGetAndSetters(List classes) {
518: HashMap map = new HashMap();
519:
520: ClassMetaData[] allMetas = metaData.classes;
521: for (int i = 0; i < allMetas.length; i++) {
522: ClassMetaData meta = allMetas[i];
523: if (!meta.isInHeirachy()) {
524: orderList.add(meta.qname);
525: } else if (meta.top.equals(meta)) {
526: getOrder(meta);
527: }
528: }
529:
530: for (Iterator iterator = orderList.iterator(); iterator
531: .hasNext();) {
532: String s = (String) iterator.next();
533: Iterator classIter = classes.iterator();
534: while (classIter.hasNext()) {
535: ClassInfo classInfo = (ClassInfo) classIter.next();
536: String className = classInfo.getClassName();
537: if (className.equals(s)) {
538: Iterator iter = classInfo.getFieldList().iterator();
539: while (iter.hasNext()) {
540: FieldInfo fieldInfo = (FieldInfo) iter.next();
541: String fieldName = fieldInfo.getFieldName();
542: SwapFieldHelper helper = new SwapFieldHelper();
543: helper.className = className;
544: helper.fieldName = fieldName;
545: helper.jdoGetName = fieldInfo.getJdoGetName();
546: helper.jdoSetName = fieldInfo.getJdoSetName();
547: helper.type = fieldInfo.getType();
548: map.put(className + "|" + fieldName, helper);
549:
550: if (!fieldInfo.isPrivate()) {
551: ClassMetaData clMeta = metaData
552: .getClassMetaData(className);
553: hierList = new ArrayList();
554: getHier(clMeta);
555: for (Iterator iterator1 = hierList
556: .iterator(); iterator1.hasNext();) {
557: String clName = (String) iterator1
558: .next();
559: map.put(clName + "|" + fieldName,
560: helper);
561: }
562: }
563: }
564:
565: }
566: }
567:
568: }
569: return map;
570: }
571:
572: private HashMap getNonPrivateMapOfGetAndSetters(List classes) {
573: HashMap map = new HashMap();
574:
575: for (Iterator iterator = orderList.iterator(); iterator
576: .hasNext();) {
577: String s = (String) iterator.next();
578: Iterator classIter = classes.iterator();
579: while (classIter.hasNext()) {
580: ClassInfo classInfo = (ClassInfo) classIter.next();
581: String className = classInfo.getClassName();
582: if (className.equals(s)) {
583: Iterator iter = classInfo.getFieldList().iterator();
584: while (iter.hasNext()) {
585: FieldInfo fieldInfo = (FieldInfo) iter.next();
586: if (fieldInfo.isPrivate())
587: continue;
588: String fieldName = fieldInfo.getFieldName();
589: SwapFieldHelper helper = new SwapFieldHelper();
590: helper.className = className;
591: helper.fieldName = fieldName;
592: helper.jdoGetName = fieldInfo.getJdoGetName();
593: helper.jdoSetName = fieldInfo.getJdoSetName();
594: helper.type = fieldInfo.getType();
595: map.put(className + "|" + fieldName, helper);
596:
597: ClassMetaData clMeta = metaData
598: .getClassMetaData(className);
599: hierList = new ArrayList();
600: getHier(clMeta);
601: for (Iterator iterator1 = hierList.iterator(); iterator1
602: .hasNext();) {
603: String clName = (String) iterator1.next();
604: map.put(clName + "|" + fieldName, helper);
605: }
606: }
607: }
608: }
609: }
610: return map;
611: }
612:
613: private ModelMetaData getMetaData(File configFile,
614: ClassLoader loader) throws Exception {
615: // parse the config
616: ConfigInfo config = new ConfigParser()
617: .parseResource(configFile);
618: config.validate();
619: return getMetaDataImp(config, loader);
620: }
621:
622: private ModelMetaData getMetaData(String configFilename,
623: ClassLoader loader) throws Exception {
624: // parse the config
625: ConfigInfo config = new ConfigParser().parseResource(
626: configFilename, loader);
627: config.validate();
628: return getMetaDataImp(config, loader);
629: }
630:
631: private ModelMetaData getMetaData(Properties props,
632: ClassLoader loader) throws Exception {
633: // parse the properties
634: ConfigInfo config = new ConfigParser().parse(props);
635: config.validate();
636: return getMetaDataImp(config, loader);
637: }
638:
639: private ModelMetaData getMetaDataImp(ConfigInfo config,
640: ClassLoader loader) throws Exception {
641:
642: if (srcOutDir != null) {
643: config.hyperdriveSrcDir = srcOutDir.toString();
644: }
645: if (hyperdriveDir != null) {
646: config.hyperdriveClassDir = hyperdriveDir.toString();
647: } else if (genHyper) {
648: config.hyperdriveClassDir = outputDirectory.toString();
649: }
650: config.hyperdrive = srcOutDir != null || hyperdriveDir != null;
651: if (this .genHyper && !config.hyperdrive) {
652: config.hyperdrive = true;
653: }
654:
655: StorageManagerFactoryBuilder b = new StorageManagerFactoryBuilder();
656: b.setConfig(config);
657: b.setLoader(loader);
658: b.setOnlyMetaData(true);
659: StorageManagerFactory smf = b.createStorageManagerFactory();
660: metaData = smf.getModelMetaData();
661:
662: Thread.currentThread().setContextClassLoader(loader);
663: return metaData;
664: }
665:
666: }
|