All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.persistence.meta.AnnotationProcessor6 Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.openjpa.persistence.meta;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import jakarta.persistence.metamodel.StaticMetamodel;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.MetaDataFactory;
import org.apache.openjpa.persistence.PersistenceMetaDataFactory;
import org.apache.openjpa.persistence.PersistentCollection;
import org.apache.openjpa.persistence.util.SourceCode;


/**
 * Annotation processing tool generates source code for a meta-model class given
 * the annotated source code of persistent entity.
 * 

* This tool is invoked during compilation for JDK6 compiler if *

    *
  • OpenJPA and JPA libraries are available in the compiler classpath * and
  • Annotation Processor option -Aopenjpa.metamodel=true is specified. *
*
* Usage
* $ javac -classpath path/to/openjpa-all.jar -Aopenjpa.metamodel=true mypackage/MyEntity.java
* will generate source code for canonical meta-model class mypackage.MyEntity_.java. * The source code is generated relative to the directory specified in -s option * of javac compiler and defaulted to the current directory. *

* The Annotation Processor also recognizes the following options (none of them are mandatory):
*

*
-Aopenjpa.log={log level}The logging level. Default is WARN. Permissible values are * TRACE, INFO, WARN or ERROR. *
-Aopenjpa.source={n} Java source version of the generated code. Default is 6. *
-Aopenjpa.naming={class name} fully-qualified name of a class implementing * org.apache.openjpa.meta.MetaDataFactory that determines * the name of a meta-class given the name of the original persistent Java entity class. Defaults to * org.apache.openjpa.persistence.PersistenceMetaDataFactory which appends a underscore character * (_) to the original Java class name. *
-Aopenjpa.header={url} * A url whose content will appear as comment header to the generated file(s). Recognizes special value * ASL for Apache Source License header as comment. By default adds a OpenJPA proprietary * text. *
*
* * @author Pinaki Poddar * * @since 2.0.0 * */ @SupportedAnnotationTypes({ "jakarta.persistence.Entity", "jakarta.persistence.Embeddable", "jakarta.persistence.MappedSuperclass" }) @SupportedOptions({ "openjpa.log", "openjpa.source", "openjpa.naming", "openjpa.header", "openjpa.metamodel", "openjpa.addGeneratedAnnotation" }) public class AnnotationProcessor6 extends AbstractProcessor { private SourceAnnotationHandler handler; private MetaDataFactory factory; private int generatedSourceVersion = 6; private CompileTimeLogger logger; private List header = new ArrayList<>(); private boolean active; private static Localizer _loc = Localizer.forPackage(AnnotationProcessor6.class); private SourceVersion supportedSourceVersion; private String addGeneratedOption; private Class generatedAnnotation; private Date generationDate; /** * Category of members as per JPA 2.0 type system. * */ private static enum TypeCategory { ATTRIBUTE("jakarta.persistence.metamodel.SingularAttribute"), COLLECTION("jakarta.persistence.metamodel.CollectionAttribute"), SET("jakarta.persistence.metamodel.SetAttribute"), LIST("jakarta.persistence.metamodel.ListAttribute"), MAP("jakarta.persistence.metamodel.MapAttribute"); private String type; private TypeCategory(String type) { this.type = type; } public String getMetaModelType() { return type; } } /** * Enumerates available java.util.* collection classes to categorize them * into corresponding JPA meta-model member type. */ private static List CLASSNAMES_LIST = Arrays.asList( new String[]{ "java.util.List", "java.util.AbstractList", "java.util.AbstractSequentialList", "java.util.ArrayList", "java.util.Stack", "java.util.Vector"}); private static List CLASSNAMES_SET = Arrays.asList( new String[]{ "java.util.Set", "java.util.AbstractSet", "java.util.EnumSet", "java.util.HashSet", "java.util.LinkedList", "java.util.LinkedHashSet", "java.util.SortedSet", "java.util.TreeSet"}); private static List CLASSNAMES_MAP = Arrays.asList( new String[]{ "java.util.Map", "java.util.AbstractMap", "java.util.EnumMap", "java.util.HashMap", "java.util.Hashtable", "java.util.IdentityHashMap", "java.util.LinkedHashMap", "java.util.Properties", "java.util.SortedMap", "java.util.TreeMap"}); private static List CLASSNAMES_COLLECTION = Arrays.asList( new String[]{ "java.util.Collection", "java.util.AbstractCollection", "java.util.AbstractQueue", "java.util.Queue", "java.util.PriorityQueue"}); /** * Gets the fully-qualified name of member class in JPA 2.0 type system, * given the fully-qualified name of a Java class. * */ private TypeCategory toMetaModelTypeCategory(TypeMirror mirror, String name, boolean persistentCollection) { if (mirror.getKind() == TypeKind.ARRAY && persistentCollection ) { return TypeCategory.LIST; } if (CLASSNAMES_COLLECTION.contains(name)) return TypeCategory.COLLECTION; if (CLASSNAMES_LIST.contains(name)) return TypeCategory.LIST; if (CLASSNAMES_SET.contains(name)) return TypeCategory.SET; if (CLASSNAMES_MAP.contains(name)) return TypeCategory.MAP; return TypeCategory.ATTRIBUTE; } @Override public SourceVersion getSupportedSourceVersion() { if (supportedSourceVersion != null) { return supportedSourceVersion; } return SourceVersion.latestSupported(); } /** * Initialization. */ @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); active = "true".equalsIgnoreCase(getOptionValue("openjpa.metamodel")); if (!active) return; final String supported = getOptionValue("openjpa.processor.supportedversion"); if (supported != null) { supportedSourceVersion = SourceVersion.valueOf(supported); } else { // default to ensure we don't log a false warning for every compilation, see OPENJPA-2300 supportedSourceVersion = SourceVersion.latestSupported(); } processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, _loc.get("mmg-tool-banner").toString()); logger = new CompileTimeLogger(processingEnv, getOptionValue("openjpa.log")); setSourceVersion(); setNamingPolicy(); setHeader(); handler = new SourceAnnotationHandler(processingEnv, logger); setAddGeneratedAnnotation(); this.generationDate = new Date(); } /** * The entry point for java compiler. */ @Override public boolean process(Set annos, RoundEnvironment roundEnv) { if (active && !roundEnv.processingOver()) { Set elements = roundEnv.getRootElements(); for (Element e : elements) { if (e instanceof TypeElement) { process((TypeElement) e); } } } return true; } /** * Generate meta-model source code for the given type. * * @return true if code is generated for the given element. false otherwise. */ private boolean process(TypeElement e) { if (!handler.isAnnotatedAsEntity(e)) { return false; } Elements eUtils = processingEnv.getElementUtils(); String originalClass = eUtils.getBinaryName((TypeElement) e).toString(); String originalSimpleClass = e.getSimpleName().toString(); String metaClass = factory.getMetaModelClassName(originalClass); SourceCode source = new SourceCode(metaClass); comment(source); annotate(source, originalClass); TypeElement supCls = handler.getPersistentSupertype(e); if (supCls != null) { String superName = factory.getMetaModelClassName(supCls.toString()); source.getTopLevelClass().setSuper(superName); } try { PrintWriter writer = createSourceFile(originalClass, metaClass, e); SourceCode.Class modelClass = source.getTopLevelClass(); Set members = handler.getPersistentMembers(e); for (Element m : members) { boolean isPersistentCollection = m.getAnnotation(PersistentCollection.class) != null; TypeMirror decl = handler.getDeclaredType(m); String fieldName = handler.getPersistentMemberName(m); String fieldType = handler.getDeclaredTypeName(decl, true, isPersistentCollection); TypeCategory typeCategory = toMetaModelTypeCategory(decl, fieldType, isPersistentCollection); String metaModelType = typeCategory.getMetaModelType(); SourceCode.Field modelField = null; switch (typeCategory) { case ATTRIBUTE: modelField = modelClass.addField(fieldName, metaModelType); modelField.addParameter(originalSimpleClass) .addParameter(fieldType); break; case COLLECTION: case LIST: case SET: TypeMirror param = handler.getTypeParameter(m, decl, 0, true); String elementType = handler.getDeclaredTypeName(param); modelField = modelClass.addField(fieldName, metaModelType); modelField.addParameter(originalSimpleClass) .addParameter(elementType); break; case MAP: TypeMirror key = handler.getTypeParameter(m, decl, 0, false); TypeMirror value = handler.getTypeParameter(m, decl, 1, true); String keyType = handler.getDeclaredTypeName(key); String valueType = handler.getDeclaredTypeName(value); modelField = modelClass.addField(fieldName, metaModelType); modelField.addParameter(originalSimpleClass) .addParameter(keyType) .addParameter(valueType); break; } modelField.makePublic().makeStatic().makeVolatile(); } source.write(writer); writer.flush(); writer.close(); return true; } catch (Exception e1) { logger.error(_loc.get("mmg-process-error", e.getQualifiedName()), e1); return false; } } private void annotate(SourceCode source, String originalClass) { SourceCode.Class cls = source.getTopLevelClass(); cls.addAnnotation(StaticMetamodel.class.getName()) .addArgument("value", originalClass + ".class", false); switch (this.addGeneratedOption) { case "false": return; case "force": cls.addAnnotation(jakarta.annotation.Generated.class.getName()) .addArgument("value", this.getClass().getName()) .addArgument("date", this.generationDate.toString()); break; case "auto": // fall through default: // only add the annotation if it is on the classpath for Java 6+. if (generatedAnnotation != null && generatedSourceVersion >= 6) { cls.addAnnotation(generatedAnnotation.getName()) .addArgument("value", this.getClass().getName()) .addArgument("date", this.generationDate.toString()); } break; } } private void comment(SourceCode source) { if (header.size() != 0) source.addComment(false, header.toArray(new String[header.size()])); String defaultHeader = _loc.get("mmg-tool-sign").getMessage(); source.addComment(false, defaultHeader); } /** * Parse annotation processor option -Aopenjpa.source=n to detect * the source version for the generated classes. * n must be a integer. Default or wrong specification returns 6. */ private void setSourceVersion() { String version = getOptionValue("openjpa.source"); if (version != null) { try { generatedSourceVersion = Integer.parseInt(version); } catch (NumberFormatException e) { logger.warn(_loc.get("mmg-bad-source", version, 6)); generatedSourceVersion = 6; } } else { generatedSourceVersion = 6; } } private void setNamingPolicy() { String policy = getOptionValue("openjpa.naming"); if (policy != null) { try { factory = (MetaDataFactory)Class.forName(policy).newInstance(); } catch (Throwable e) { logger.warn(_loc.get("mmg-bad-naming", policy, e)); factory = new PersistenceMetaDataFactory(); } } else { factory = new PersistenceMetaDataFactory(); } } private void setHeader() { String headerOption = getOptionValue("openjpa.header"); if (headerOption == null) { return; } if ("ASL".equalsIgnoreCase(headerOption)) { header.add(_loc.get("mmg-asl-header").getMessage()); } else { try { URL url = new URL(headerOption); InputStream is = url.openStream(); Scanner s = new Scanner(is); while (s.hasNextLine()) { header.add(s.nextLine()); } } catch (Throwable t) { } } } private void setAddGeneratedAnnotation() { this.addGeneratedOption = getOptionValue("openjpa.addGeneratedAnnotation"); if (this.addGeneratedOption == null) { this.addGeneratedOption = "auto"; } // only add the annotation if it is on the classpath for Java 6+. try { this.generatedAnnotation = Class.forName("jakarta.annotation.Generated", false, null); } catch (ClassNotFoundException generatedNotFoundEx) { logger.trace(_loc.get("mmg-annotation-not-found")); } } /** * 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 = processingEnv.getFiler().createSourceFile(metaClass, e); logger.info(_loc.get("mmg-process", javaFile.toUri().normalize())); return new PrintWriter(javaFile.openWriter()); } /** * Get the value for the given keys, whoever matches first, in the current available options. */ private String getOptionValue(String... keys) { Map options = processingEnv.getOptions(); for (String key : keys) { if (options.containsKey(key)) return options.get(key); } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy