Author: ppoddar
Date: Wed Jul 22 15:33:38 2009
New Revision: 796768

URL: http://svn.apache.org/viewvc?rev=796768&view=rev
Log:
OPENJPA-1187: Allow output be generated in the same directory of the source 
files.

Modified:
    
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AnnotationProcessor6.java
    
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/CompileTimeLogger.java
    
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/SourceAnnotationHandler.java
    
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AnnotationProcessor6.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AnnotationProcessor6.java?rev=796768&r1=796767&r2=796768&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AnnotationProcessor6.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AnnotationProcessor6.java
 Wed Jul 22 15:33:38 2009
@@ -24,12 +24,12 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URL;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Generated;
@@ -57,28 +57,42 @@
 import org.apache.openjpa.persistence.util.SourceCode;
 
 /**
- * Annotation processing tool generates souce code for a meta-model class 
given 
+ * Annotation processing tool generates source code for a meta-model class 
given 
  * the annotated source code of persistent entity.
  * <p>
  * This tool is invoked during compilation for JDK6 compiler if OpenJPA and 
JPA 
  * libraries are specified in the compiler <code>-processorpath</code> option.
  * <br>
- * Supported options
- * <LI>
- * For example,<br>
- * <center><code>$ javac -processorpath path/to/openjpa;/path/to/jpa 
- * -s src -Alog mypackage/MyClass.java</code></center>
- * <p> 
- * will generate source code for canonical meta-model class  at
- * <code>src/mypackage/MyClass_.java</code>.
+ * <B>Usage</B><br>
+ * <code>$ javac -processorpath path/to/openjpa-all.jar 
mypackage/MyEntity.java</code><br>
+ * will generate source code for canonical meta-model class 
<code>mypackage.MyEntity_.java</code>.
  * <p>
- * The generated source code is written relative to the source path root which
- * is, by default, the current directory or as specified by -s option to 
- * <code>javac</code> compiler. 
- * <p>
- * Currently the only recognized option is <code>-Alog</code> specified as 
shown
- * in the <code>javac</code> command above.
- * 
+ * The Annotation Processor recognizes the following options (none of them are 
mandatory):
+ * <LI><code>-Alog=TRACE|INFO|WARN|ERROR</code><br>
+ * The logging level. Default is <code>WARN</code>.
+ * <LI>-Asource=&lt;n&gt;<br>
+ * where &lt;n&gt; denotes the integral number for Java source version of the 
generated code. 
+ * Default is <code>6</code>.
+ * <LI>-Anaming=class name <br>
+ * fully-qualified name of a class implementing 
<code>org.apache.openjpa.meta.MetaDataFactory</code> that determines
+ * the name of a meta-class given the name of the original persistent Java 
entity class. Defaults to
+ * <code>org.apache.openjpa.persistence.PersistenceMetaDataFactory</code> 
which appends a underscore character
+ * (<code>_</code>) to the original Java class name. 
+ * <LI>-Aheader=&lt;url&gt;<br>
+ * A url whose content will appear as comment header to the generated file(s). 
Recognizes special value
+ * <code>ASL</code> for Apache Source License header as comment. By default 
adds a OpenJPA proprietary   
+ * text.
+ * <LI>-Aout=dir<br>
+ * A directory in the local file system. The generated files will be written 
<em>relative</em> to this directory
+ * according to the package structure i.e. if <code>dir</code> is specified as 
<code>/myproject/generated-src</code>
+ * then the generated source code will be written to 
<code>/myproject/generated-src/mypackage/MyEntity_.java</code>.
+ * If this option is not specified, then an attempt will be made to write the 
generated source file in the same
+ * directory of the source code of original class 
<code>mypackage.MyEntity</code>. The source code location for 
+ * <code>mypackage.MyEntity</code> can only be determined for Sun JDK6 and 
<code>tools.jar</code> being available 
+ * to the compiler classpath. If the source code location for the original 
class can not be determined, and the 
+ * option is not specified, then the generated source code is written relative 
to the current directory according 
+ * to the package structure.  
+ * <br>
  * @author Pinaki Poddar
  * 
  * @since 2.0.0
@@ -94,10 +108,11 @@
 public class AnnotationProcessor6 extends AbstractProcessor {
     private SourceAnnotationHandler handler;
     private StandardJavaFileManager fileManager;
+    private boolean isUserSpecifiedOutputLocation = false;
     private MetaDataFactory factory;
     private int generatedSourceVersion = 6;
     private CompileTimeLogger logger;
-    private boolean addHeader = false;
+    private String header;
     private static Localizer _loc =  
Localizer.forPackage(AnnotationProcessor6.class);
 
     /**
@@ -180,7 +195,7 @@
         setSourceVersion();
         setFileManager();
         setNamingPolicy();
-        addHeader = 
"true".equalsIgnoreCase(processingEnv.getOptions().get("header"));
+        setHeader();
         handler = new SourceAnnotationHandler(processingEnv, logger);
     }
     
@@ -224,7 +239,7 @@
             source.getTopLevelClass().setSuper(superName);
         }
         try {
-            PrintWriter writer = createSourceFile(metaClass, e);
+            PrintWriter writer = createSourceFile(originalClass, metaClass, e);
             SourceCode.Class modelClass = source.getTopLevelClass();
             Set<? extends Element> members = handler.getPersistentMembers(e);
             
@@ -269,8 +284,9 @@
             writer.flush();
             writer.close();
             return true;
-        } catch (IOException e1) {
-            throw new RuntimeException(e1);
+        } catch (Exception e1) {
+            logger.error(_loc.get("mmg-process-error", e.getQualifiedName()), 
e1);
+            return false;
         } finally {
 
         }
@@ -288,11 +304,10 @@
     }
     
     private void comment(SourceCode source) {
-        if (addHeader) {
-           source.addComment(false, _loc.get("mmg-asl-header").getMessage());
-        } else {
-            source.addComment(false, _loc.get("mmg-tool-sign").getMessage());
-        }
+        if (header != null)
+            source.addComment(false, header);
+        String defaultHeader = _loc.get("mmg-tool-sign").getMessage();
+        source.addComment(false, defaultHeader);
     }
     
     /**
@@ -328,31 +343,162 @@
         }
     }
     
-    private void setFileManager() {
-        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        fileManager = compiler.getStandardFileManager(null, 
-            null, null);
-        String srcOutput = processingEnv.getOptions().get("out");
-        if (srcOutput != null) {
+    private void setHeader() {
+        String headerOption = processingEnv.getOptions().get("header");
+        if (headerOption == null) {
+            return;
+        }
+        if ("ASL".equalsIgnoreCase(headerOption)) {
+            header = _loc.get("mmg-asl-header").getMessage();
+        } else {
             try {
-                fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, 
-                    Collections.singletonList(new File(srcOutput)));
+                URL url = new URL(headerOption);
+                header = url.getContent().toString();
             } catch (Throwable t) {
-                logger.warn(_loc.get("mmg-bad-out", srcOutput, 
-                    StandardLocation.SOURCE_OUTPUT));
+                
             }
         }
     }
+    
+    private void setFileManager() {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        fileManager = compiler.getStandardFileManager(null, null, null);
+        String outDir = processingEnv.getOptions().get("out");
+        if (outDir != null)
+           isUserSpecifiedOutputLocation = setSourceOutputDirectory(new 
File(outDir));
+    }
 
-    private PrintWriter createSourceFile(String metaClass, TypeElement e) 
+    /**
+     * Creates a file where source code of the given metaClass will be written.
+     * 
+     */
+    private PrintWriter createSourceFile(String originalClass, String 
metaClass, TypeElement e) 
         throws IOException {
-        
-        JavaFileObject javaFile = fileManager.getJavaFileForOutput(
-            StandardLocation.SOURCE_OUTPUT, 
-            metaClass, JavaFileObject.Kind.SOURCE, null);
+        if (!isUserSpecifiedOutputLocation) {
+            
setSourceOutputDirectory(OutputPath.getAbsoluteDirectory(processingEnv, e));
+        }
+        JavaFileObject javaFile = 
fileManager.getJavaFileForOutput(StandardLocation.SOURCE_OUTPUT, 
+            metaClass, JavaFileObject.Kind.SOURCE, 
+            null); // do not use sibling hint because of indeterminable 
behavior across JDK 
         logger.info(_loc.get("mmg-process", javaFile.toUri()));
         OutputStream out = javaFile.openOutputStream();
         PrintWriter writer = new PrintWriter(out);
         return writer;
     }
+    
+    /**
+     * Sets the output directory for generated source files.
+     * Tries to create the directory structure if does not exist.
+     * 
+     * @return true if the output has been set successfully.
+     */
+    boolean setSourceOutputDirectory(File outDir) {
+        if (outDir == null)
+            return false;
+        if (!outDir.exists()) {
+            if (!outDir.mkdirs()) {
+                logger.warn(_loc.get("mmg-bad-out", outDir, 
StandardLocation.SOURCE_OUTPUT));
+                return false;
+            }
+        }
+        try {
+            fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, 
Collections.singleton(outDir));
+            return true;
+        } catch (IOException e) {
+            logger.warn(_loc.get("mmg-bad-out", outDir, 
StandardLocation.SOURCE_OUTPUT));
+            return false;
+        }
+    }
+    
+    /**
+     * An utility class to determine the source file corresponding to a 
{...@link TypeElement}.
+     * The utility uses Sun JDK internal API (com.sun.tools.*) and hence works 
reflectively
+     * to avoid compile-time dependency.
+     *   
+     * @author Pinaki Poddar
+     *
+     */
+    public static class OutputPath {
+        private static Class<?> trees = null;
+        static {
+            try {
+                trees = Class.forName("com.sun.source.util.Trees");
+            } catch (Throwable t) {
+                
+            }
+        }
+        
+        /**
+         * Gets the directory relative to the Java source file corresponding 
to the TypeElement.
+         * 
+         * @return null if the com.sun.source.util.* package is not available 
or the given TypeElement
+         * does not correspond to a compilation unit associated to a source 
file.
+         */
+        public static File getAbsoluteDirectory(ProcessingEnvironment env, 
TypeElement e) {
+            if (trees == null)
+                return null;
+            try {
+                // Trees root = Trees.instance(env);
+                Object root = trees.getMethod("instance", new 
Class[]{ProcessingEnvironment.class})
+                    .invoke(null, env);
+                
+                // TreePath path = root.getPath(e);
+                Object path = root.getClass().getMethod("getPath", new 
Class[]{Element.class})
+                    .invoke(root, e);
+                
+                // CompilationUnitTree unit = path.getCompilationUnit();
+                Object unit = path.getClass().getMethod("getCompilationUnit", 
(Class[])null)
+                    .invoke(path, (Object[])null);
+                
+                // JavaFileObject f = unit.getSourceFile();
+                JavaFileObject f = 
(JavaFileObject)unit.getClass().getMethod("getSourceFile", (Class[])null)
+                    .invoke(unit, (Object[])null);
+                
+                URI uri = f.toUri();
+                File dir = getParentFile(new File(uri.toURL().getPath()), 
packageDepth(e.getQualifiedName().toString()));
+                return dir;
+            } catch (Throwable t) {
+                return null;
+            }
+        }
+        
+        /**
+         * Gets the parent of the given file recursively traversing to given 
number of levels.
+         */
+        public static File getParentFile(File f, int n) {
+            if (n < 0)
+                return f;
+            if (n == 0)
+                return f.getParentFile();
+            return getParentFile(f.getParentFile(), n-1);
+        }
+        
+        public static int packageDepth(String s) {
+            String pkg = getPackageName(s);
+            if (pkg == null)
+                return 0;
+            int depth = 1;
+            int i = 0;
+            while ((i = pkg.indexOf('.')) != -1) {
+                depth++;
+                pkg = pkg.substring(i+1);
+            }
+            return depth;
+        }
+        
+        public static String getPackageName(String s) {
+            if (s == null)
+                return null;
+            int i = s.lastIndexOf('.');
+            return (i == -1) ? null : s.substring(0, i);
+        }
+        
+        public static String getSimpleName(String s) {
+            if (s == null)
+                return null;
+            int i = s.lastIndexOf('.');
+            return (i == -1) ? s : s.substring(i+1);
+        }
+    }
+
 }

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/CompileTimeLogger.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/CompileTimeLogger.java?rev=796768&r1=796767&r2=796768&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/CompileTimeLogger.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/CompileTimeLogger.java
 Wed Jul 22 15:33:38 2009
@@ -75,7 +75,13 @@
     }
     
     public void error(Localizer.Message message) {
+        error(message, null);
+    }
+    
+    public void error(Localizer.Message message, Throwable t) {
         log(Level.ERROR, message, Diagnostic.Kind.ERROR);
+        if (t != null)
+            t.printStackTrace();
     }
     
     private void log(Level level, Localizer.Message message, 

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/SourceAnnotationHandler.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/SourceAnnotationHandler.java?rev=796768&r1=796767&r2=796768&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/SourceAnnotationHandler.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/SourceAnnotationHandler.java
 Wed Jul 22 15:33:38 2009
@@ -544,7 +544,10 @@
        mirror = box ? box(mirror) : mirror;
        if (isPrimitive(mirror))
                return ((PrimitiveType)mirror).toString();
-        return processingEnv.getTypeUtils().asElement(mirror).toString();
+       Element elem = processingEnv.getTypeUtils().asElement(mirror);
+       if (elem == null)
+           throw new RuntimeException(_loc.get("mmg-no-type", 
mirror).getMessage());
+        return elem.toString();
     }
 
     /**

Modified: 
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties?rev=796768&r1=796767&r2=796768&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties
 Wed Jul 22 15:33:38 2009
@@ -38,6 +38,9 @@
 getter-unmatched: Getter method "{0}" in "{1}" has no matching setter method.
 mmg-tool-banner: Starting OpenJPA Annotation Processor for Metamodel 
Generation 
 mmg-process: Generating canonical metamodel source code "{0}"
+mmg-process-error: Error while generating metamodel for "{0}". See exception \
+       stack trace for details.
+mmg-no-type: Type {0} not found. This may happen if {0} is not compiled. 
 mmg-tool-sign: Generated by OpenJPA MetaModel Generator Tool.
 mmg-bad-source: Wrong value "{0}" of -Asource option to specify the target \
        Java compiler version for the generated meta-model files. Expected a \


Reply via email to