blob: 2dea93d575fbd3ca63dde2945a23a8d581ce34cd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2010 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.gen.internal;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.log.JdkLogChute;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jpt.common.utility.internal.CollectionTools;
import org.eclipse.jpt.jpa.core.JpaProject;
import org.eclipse.jpt.jpa.core.context.persistence.Persistence;
import org.eclipse.jpt.jpa.core.context.persistence.PersistenceUnit;
import org.eclipse.jpt.jpa.core.resource.xml.JpaXmlResource;
import org.eclipse.jpt.jpa.gen.internal.util.CompilationUnitModifier;
import org.eclipse.jpt.jpa.gen.internal.util.FileUtil;
import org.eclipse.jpt.jpa.gen.internal.util.UrlUtil;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
/**
* This generator will generate a package of entities for a set of tables.
*/
public class PackageGenerator {
private static final String LOGGER_NAME = "org.eclipse.jpt.entities.gen.log"; //$NON-NLS-1$
private final JpaProject jpaProject;
private final ORMGenCustomizer customizer;
private final OverwriteConfirmer overwriteConfirmer;
public static void generate(JpaProject jpaProject, ORMGenCustomizer customizer, OverwriteConfirmer overwriteConfirmer, IProgressMonitor monitor) throws CoreException {
SubMonitor sm = SubMonitor.convert(monitor, 20);
PackageGenerator generator = new PackageGenerator(jpaProject, customizer, overwriteConfirmer);
sm.worked(1);
try {
generator.doGenerate(sm.newChild(19));
} catch (Exception e) {
throw new CoreException(new Status(IStatus.ERROR, JptJpaGenPlugin.PLUGIN_ID, JptGenMessages.Error_Generating_Entities, e));
}
}
private PackageGenerator(JpaProject jpaProject, ORMGenCustomizer customizer, OverwriteConfirmer confirmer) {
super();
this.jpaProject = jpaProject;
this.customizer = customizer;
this.overwriteConfirmer = confirmer;
}
private Object getCustomizer() {
return this.customizer;
}
private IJavaProject getJavaProject(){
return this.jpaProject.getJavaProject();
}
protected void doGenerate(IProgressMonitor monitor) throws Exception {
generateInternal(monitor);
}
protected void generateInternal(IProgressMonitor monitor) throws Exception {
File templDir = prepareTemplatesFolder();
List<String> genClasses = new java.util.ArrayList<String>();
List<String> tableNames = this.customizer.getGenTableNames();
/* .java per table, persistence.xml, refresh package folder */
SubMonitor sm = SubMonitor.convert(monitor, tableNames.size() + 2);
for (Iterator<String> iter = tableNames.iterator(); iter.hasNext();) {
if (sm.isCanceled()) {
return;
}
String tableName = iter.next();
ORMGenTable table = this.customizer.getTable(tableName);
String className = table.getQualifiedClassName();
generateClass(table, templDir.getAbsolutePath(), sm.newChild(1, SubMonitor.SUPPRESS_NONE));
genClasses.add(className);
/*
* add the composite key class to persistence.xml because some
* JPA provider(e.g. Kodo) requires it. Hibernate doesn't seem to care).
*/
if (table.isCompositeKey()) {
genClasses.add(table.getQualifiedCompositeKeyClassName());
}
}
if (sm.isCanceled()) {
return;
}
//update persistence.xml
if (this.customizer.updatePersistenceXml()) {
updatePersistenceXml(genClasses);
}
sm.worked(2);
}
private void updatePersistenceXml(final List<String> genClasses) {
JpaXmlResource resource = this.jpaProject.getPersistenceXmlResource();
if (resource == null) {
//the resource would only be null if the persistence.xml file had an invalid content type,
//do not attempt to update
return;
}
resource.modify(new Runnable() {
public void run() {
Persistence persistence = PackageGenerator.this.jpaProject.getRootContextNode().getPersistenceXml().getPersistence();
if (persistence == null) {
// invalid content, do not attempt to update
return;
}
PersistenceUnit persistenceUnit;
// create a persistence unit if one doesn't already exist
if (persistence.persistenceUnitsSize() == 0) {
persistenceUnit = persistence.addPersistenceUnit();
persistenceUnit.setName(PackageGenerator.this.jpaProject.getName());
} else {
// we only support one persistence unit - take the first one
persistenceUnit = persistence.persistenceUnits().next();
}
for (Iterator<String> stream = genClasses.iterator(); stream.hasNext();) {
String className = stream.next();
if (CollectionTools.isEmpty(persistenceUnit.mappingFileRefsContaining(className)) && !persistenceUnit.specifiesPersistentType(className)) {
persistenceUnit.addSpecifiedClassRef(className);
}
}
}
});
}
private File prepareTemplatesFolder() throws IOException, Exception,
CoreException {
//Prepare the Velocity template folder:
//If the plug-in is packaged as a JAR, we need extract the template
//folder into the plug-in state location. This is required by Velocity
//since we use included templates.
Bundle bundle = Platform.getBundle(JptJpaGenPlugin.PLUGIN_ID);
String templatesPath = "templates/entities/"; //$NON-NLS-1$
Path path = new Path( templatesPath);
URL url = FileLocator.find(bundle, path, null);
if (url == null) {
throw new CoreException(new Status(IStatus.ERROR, JptJpaGenPlugin.PLUGIN_ID, JptGenMessages.Templates_notFound + " "+ JptJpaGenPlugin.PLUGIN_ID + "/" + templatesPath) );//$NON-NLS-1$
}
URL templUrl = FileLocator.resolve(url);
//Have this check so that the code would work in both PDE and JARed plug-in at runtime
File templDir = null;
if (UrlUtil.isJarUrl(templUrl)) {
templDir = FileUtil.extractFilesFromBundle( templUrl, bundle, templatesPath );
} else {
templDir = UrlUtil.getUrlFile(templUrl);
}
if (templDir == null || !templDir.exists()) {
throw new CoreException(new Status(IStatus.ERROR, JptJpaGenPlugin.PLUGIN_ID, JptGenMessages.Templates_notFound + " "+ JptJpaGenPlugin.PLUGIN_ID ) );//$NON-NLS-1$
}
return templDir;
}
/**
* Saves/Creates the .java file corresponding to a database table
* with the given content.
*
* @param templDir The velocity template file directory. It is assumed
* that this directory contains the 2 files <em>main.java.vm</em>
* and <em>pk.java.vm</em>
* @param progress
*/
protected void generateClass(ORMGenTable table, String templateDirPath, IProgressMonitor monitor) throws Exception {
String subTaskName = NLS.bind(JptGenMessages.EntityGenerator_taskName, table.getName());
SubMonitor sm = SubMonitor.convert(monitor, subTaskName, 10);
try {
IFolder javaPackageFolder = getJavaPackageFolder(table, monitor);
IFile javaFile = javaPackageFolder.getFile( table.getClassName() + ".java"); //$NON-NLS-1$
if (javaFile.exists()) {
if (this.overwriteConfirmer != null && !this.overwriteConfirmer.overwrite(javaFile.getName())) {
return;
}
}
//JdkLogChute in this version of Velocity not allow to set log level
//Workaround by preset the log level before Velocity is initialized
Logger logger = Logger.getLogger( LOGGER_NAME );
logger.setLevel( Level.SEVERE );
Properties vep = new Properties();
vep.setProperty("file.resource.loader.path", templateDirPath); //$NON-NLS-1$
vep.setProperty( JdkLogChute.RUNTIME_LOG_JDK_LOGGER, LOGGER_NAME );
VelocityEngine ve = new VelocityEngine();
ve.init(vep);
sm.worked(2);
generateJavaFile(table, javaFile, ve, "main.java.vm", true/*isDomainClass*/, sm.newChild(6)); //$NON-NLS-1$
if (table.isCompositeKey()) {
IFile compositeKeyFile = javaPackageFolder.getFile( table.getCompositeKeyClassName()+".java"); //$NON-NLS-1$
generateJavaFile(table, compositeKeyFile, ve, "pk.java.vm", false/*isDomainClass*/, sm.newChild(1)); //$NON-NLS-1$
}
else {
sm.setWorkRemaining(1);
}
javaFile.refreshLocal(1, sm.newChild(1));
} catch (Throwable e) {
CoreException ce = new CoreException(new Status(IStatus.ERROR, JptJpaGenPlugin.PLUGIN_ID, JptGenMessages.Templates_notFound + "" + JptJpaGenPlugin.PLUGIN_ID , e) );//$NON-NLS-1$
JptJpaGenPlugin.logException( ce );
}
}
private void generateJavaFile(ORMGenTable table, IFile javaFile, VelocityEngine ve
, String templateName, boolean isDomainClass, IProgressMonitor monitor) throws Exception {
VelocityContext context = new VelocityContext();
context.put("table", table); //$NON-NLS-1$
context.put("customizer", getCustomizer()); //$NON-NLS-1$
StringWriter w = new StringWriter();
ve.mergeTemplate(templateName, context, w);
String fileContent = w.toString();
if (javaFile.exists()) {
if (isDomainClass) {
updateExistingDomainClass(table.getQualifiedClassName(), javaFile, fileContent);
} else {
javaFile.setContents(new ByteArrayInputStream(fileContent.getBytes()), true, true, monitor);
}
} else {
createFile(javaFile, new ByteArrayInputStream(fileContent.getBytes()));
}
}
/**
* Updates the (existing) Java file corresponding to the given class.
*
* @param className The qualified class name.
*
* @param javaFile The existing Java file of the class to update.
*
* @param fileContent The new file content.
*/
protected void updateExistingDomainClass(String className, IFile javaFile, String fileContent) throws Exception {
/*use CompilationUnitModifier instead of calling WideEnv.getEnv().setFileContent
* so that if the unit is up to date if it is used before file change
* notifications are delivered (see EJB3ImportSchemaWizard.updateExistingDomainClass for example)*/
CompilationUnitModifier modifier = new CompilationUnitModifier(this.getJavaProject(), className);
modifier.setJavaSource(fileContent);
modifier.save();
}
public void createFile(IFile file, java.io.InputStream contents) throws CoreException {
file.create(contents, false, null/*monitor*/);
}
public IFolder getJavaPackageFolder(ORMGenTable table, IProgressMonitor monitor) throws CoreException {
IPackageFragmentRoot root = getDefaultJavaSourceLocation(this.getJavaProject(), table.getSourceFolder());
String packageName = table.getPackage();
if (packageName == null) packageName = ""; //$NON-NLS-1$
IPackageFragment packageFragment = root.getPackageFragment(packageName);
if( !packageFragment.exists()){
root.createPackageFragment(packageName, true, monitor);
}
return (IFolder) packageFragment.getResource();
}
private IPackageFragmentRoot getDefaultJavaSourceLocation(IJavaProject jproject, String sourceFolder){
IPackageFragmentRoot defaultSrcPath = null;
if (jproject != null && jproject.exists()) {
try {
IPackageFragmentRoot[] roots = jproject.getPackageFragmentRoots();
for (int i = 0; i < roots.length; i++) {
if (roots[i].getKind() == IPackageFragmentRoot.K_SOURCE ){
if (defaultSrcPath == null) {
defaultSrcPath = roots[i];
}
String path = roots[i].getPath().toString();
if (path.equals('/' + sourceFolder)) {
return roots[i] ;
}
}
}
} catch (JavaModelException e) {
JptJpaGenPlugin.logException(e);
}
}
return defaultSrcPath;
}
}