blob: e5c1d14de5e8fe612df205220fbc5042ec1ae6a4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.jaxb.core.internal;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.ILaunchesListener2;
import org.eclipse.jdt.core.IJavaElement;
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.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jpt.core.internal.utility.jdt.JDTTools;
import org.eclipse.jpt.jaxb.ui.JptJaxbUiPlugin;
import org.eclipse.osgi.service.datalocation.Location;
import org.osgi.framework.Bundle;
/**
* SchemaGenerator
*/
public class SchemaGenerator
{
static public String LAUNCH_CONFIG_NAME = "JAXB Schema Gen Run Config"; //$NON-NLS-1$
static public String JAXB_SCHEMA_GEN_PACKAGE_NAME = "org.eclipse.jpt.jaxb.core.schemagen"; //$NON-NLS-1$
static public String JAXB_SCHEMA_GEN_CLASS = JAXB_SCHEMA_GEN_PACKAGE_NAME + ".Main"; //$NON-NLS-1$
static public String ECLIPSELINK_JAXB_SCHEMA_GEN_PACKAGE_NAME = "org.eclipse.jpt.eclipselink.jaxb.core.schemagen"; //$NON-NLS-1$
static public String ECLIPSELINK_JAXB_SCHEMA_GEN_CLASS = ECLIPSELINK_JAXB_SCHEMA_GEN_PACKAGE_NAME + ".Main"; //$NON-NLS-1$
static public String ECLIPSELINK_JAXB_CONTEXT_FACTORY = "org.eclipse.persistence.jaxb.JAXBContextFactory"; //$NON-NLS-1$
static public String ECLIPSELINK_JAXB_PROPERTIES_FILE_CONTENTS = "javax.xml.bind.context.factory=" + ECLIPSELINK_JAXB_CONTEXT_FACTORY; //$NON-NLS-1$
static public String JAXB_PROPERTIES_FILE_NAME = "jaxb.properties"; //$NON-NLS-1$
static public String JAXB_SCHEMA_GEN_JAR = JAXB_SCHEMA_GEN_PACKAGE_NAME + "_"; //$NON-NLS-1$
static public String ECLIPSELINK_JAXB_SCHEMA_GEN_JAR = ECLIPSELINK_JAXB_SCHEMA_GEN_PACKAGE_NAME + "_"; //$NON-NLS-1$
static public String JAXB_GENERIC_SCHEMA_GEN_CLASS = "javax.xml.bind.JAXBContext"; //$NON-NLS-1$
static public String JAXB_ECLIPSELINK_SCHEMA_GEN_CLASS = "org.eclipse.persistence.jaxb.JAXBContext"; //$NON-NLS-1$
static public String PLUGINS_DIR = "plugins/"; //$NON-NLS-1$
private IVMInstall jre;
private ILaunchConfigurationWorkingCopy launchConfig;
private ILaunch launch;
private final IJavaProject javaProject;
private final String targetSchemaName;
private final String[] sourceClassNames;
private String mainType;
private boolean useMoxy;
private final boolean isDebug = false;
// ********** static methods **********
public static void generate(
IJavaProject javaProject,
String targetSchemaName,
String[] sourceClassNames,
boolean useMoxy,
IProgressMonitor monitor) {
if (javaProject == null) {
throw new NullPointerException();
}
new SchemaGenerator(javaProject,
targetSchemaName,
sourceClassNames,
useMoxy,
monitor).generate();
}
// ********** constructors **********
protected SchemaGenerator(
IJavaProject javaProject,
String targetSchemaName,
String[] sourceClassNames,
boolean useMoxy,
@SuppressWarnings("unused") IProgressMonitor monitor) {
super();
this.javaProject = javaProject;
this.targetSchemaName = targetSchemaName;
this.sourceClassNames = sourceClassNames;
this.useMoxy = useMoxy;
this.mainType = (this.useMoxy) ?
ECLIPSELINK_JAXB_SCHEMA_GEN_CLASS :
JAXB_SCHEMA_GEN_CLASS;
this.initialize();
}
// ********** behavior **********
protected void initialize() {
try {
this.jre = this.getProjectJRE();
if (this.jre == null) {
String message = "Could not identify the VM."; //$NON-NLS-1$
throw new RuntimeException(message);
}
this.launchConfig = this.buildLaunchConfiguration();
}
catch (CoreException e) {
throw new RuntimeException(e);
}
}
protected void generate() {
// generate jaxb.properties file if necessary
if (this.useMoxy){
if (!isJaxbPropertiesFilePresent()){
this.generateJaxbPropertiesFile();
}
else if (!isJaxbContextMoxy(getJaxbPropertiesFile())){
//properties file actually specifies a different implementation
//override wizard setting and fall back to generic generation
this.useMoxy = false;
this.mainType = JAXB_SCHEMA_GEN_CLASS;
}
}
String projectLocation = getProject().getLocation().toString();
this.initializeLaunchConfiguration(projectLocation);
this.addLaunchListener();
this.launch = this.saveAndLaunchConfig();
}
/**
* Returns the first "jaxb.properties" file that is found in a valid source
* folder in the project.
*
* Returns null if no "jaxb.properties" file is found.
*/
private IFile getJaxbPropertiesFile() {
return getJaxbPropertiesFileFromPackageRoots(JDTTools.getJavaSourceFolders(this.javaProject));
}
private IFile getJaxbPropertiesFileFromPackageRoots(Iterable<IPackageFragmentRoot> packageFragmentRoots){
Object[] objects = null;
IJavaElement[] javaElements;
try {
for (IPackageFragmentRoot pfr : packageFragmentRoots) {
javaElements = pfr.getChildren();
for (IJavaElement javaElement : javaElements) {
objects = ((IPackageFragment) javaElement).getNonJavaResources();
for (Object object : objects) {
IResource resource = (IResource) object;
if (resource.getName().equals(JAXB_PROPERTIES_FILE_NAME)) {
// jaxb.properties has been found
return (IFile)resource;
}
}
}
}
} catch (JavaModelException jme) {
throw new RuntimeException(jme);
}
return null;
}
private boolean isJaxbPropertiesFilePresent(){
return getJaxbPropertiesFile()!= null;
}
private boolean isJaxbContextMoxy(IFile propertyFile){
InputStream in = null;
try {
in = propertyFile.getContents();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = reader.readLine();
//jaxb.properties will only contain one property entry, the JAXBContextFactory
String propertyValue = line.substring(line.indexOf("=") + 1);
if (propertyValue.equals(ECLIPSELINK_JAXB_CONTEXT_FACTORY)){
return true;
}
} catch (CoreException ce){
throw new RuntimeException(ce);
} catch (IOException ioe){
throw new RuntimeException(ioe);
} finally {
if (in != null){
try{
in.close();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
return false;
}
private void generateJaxbPropertiesFile(){
IPackageFragment packageFragment = findPackageFragementForSourceClassName(this.sourceClassNames[0]);
IFolder folder = (IFolder)packageFragment.getResource();
IFile file = folder.getFile("jaxb.properties");
byte[] bytes;
try {
bytes = ECLIPSELINK_JAXB_PROPERTIES_FILE_CONTENTS.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
InputStream contents = new ByteArrayInputStream(bytes);
try {
//the input stream will be closed as a result of calling create
file.create(contents, IResource.NONE, null);
} catch (CoreException ce) {
throw new RuntimeException(ce);
}
}
private IPackageFragment findPackageFragementForSourceClassName(String sourceClassName) {
String packageName = sourceClassName.substring(0, sourceClassName.lastIndexOf('.'));
//Find the existing package fragment where we want to generate
for (IPackageFragmentRoot pfr : JDTTools.getJavaSourceFolders(this.javaProject)) {
//use the package of the first source class as the package for generation
IPackageFragment packageFragment = pfr.getPackageFragment(packageName);
if (packageFragment.exists()){
return packageFragment;
}
}
//the existing package fragment was not found
throw new IllegalStateException("Java package must exist for source class");
}
private void initializeLaunchConfiguration(String projectLocation) {
this.specifyJRE(this.jre.getName(), this.jre.getVMInstallType().getId());
this.specifyProject(this.getProject().getName());
this.specifyMainType(this.mainType);
this.specifyProgramArguments(
this.targetSchemaName,
this.sourceClassNames); // -d -c
this.specifyWorkingDir(projectLocation);
String jarName = (this.useMoxy) ?
ECLIPSELINK_JAXB_SCHEMA_GEN_JAR :
JAXB_SCHEMA_GEN_JAR;
this.specifyClasspathProperties(this.javaProject, this.buildBootstrapJarPath(jarName));
}
protected void postGenerate() {
try {
if ( ! this.isDebug) {
this.removeLaunchConfiguration(LAUNCH_CONFIG_NAME);
}
this.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
}
catch (CoreException e) {
throw new RuntimeException(e);
}
}
// ********** Launch Configuration Setup **********
private void specifyClasspathProperties(IJavaProject javaProject, IPath bootstrapJar) {
List<String> classpath = new ArrayList<String>();
try {
// Schema_Gen jar
classpath.add(this.getArchiveClasspathEntry(bootstrapJar).getMemento());
// Default Project classpath
classpath.add(this.getDefaultProjectClasspathEntry(javaProject).getMemento());
// System Library
classpath.add(this.getSystemLibraryClasspathEntry().getMemento());
}
catch (CoreException e) {
throw new RuntimeException("An error occurs generating a memento", e); //$NON-NLS-1$
}
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpath);
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
}
private void specifyJRE(String jreName, String vmId) {
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, jreName);
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, vmId);
}
private void specifyProject(String projectName) {
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName);
}
private void specifyMainType(String mainType) {
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, mainType);
}
private void specifyProgramArguments(
String targetSchemaName,
String[] sourceClassNames) {
StringBuffer programArguments = new StringBuffer();
// sourceClassNames
StringBuffer sourceClassNamesArguments = this.buildClassNamesArguments(sourceClassNames);
programArguments.append(sourceClassNamesArguments);
// schema
programArguments.append(" -s \""); //$NON-NLS-1$
programArguments.append(targetSchemaName);
programArguments.append('"'); //$NON-NLS-1$
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, programArguments.toString());
}
private StringBuffer buildClassNamesArguments(String[] sourceClassNames) {
StringBuffer classNamesArguments = new StringBuffer();
for (String className: sourceClassNames) {
classNamesArguments.append(" -c "); //$NON-NLS-1$
classNamesArguments.append(className);
}
return classNamesArguments;
}
private void specifyWorkingDir(String projectLocation) {
File workingDir = new Path(projectLocation).toFile();
this.launchConfig.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, workingDir.getAbsolutePath());
}
// ********** LaunchConfig **********
private ILaunchConfigurationWorkingCopy buildLaunchConfiguration() throws CoreException {
ILaunchConfigurationWorkingCopy launchConfig = null;
this.removeLaunchConfiguration(LAUNCH_CONFIG_NAME);
ILaunchManager manager = getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
launchConfig = type.newInstance(null, LAUNCH_CONFIG_NAME);
return launchConfig;
}
private void removeLaunchConfiguration(String launchConfigurationName) throws CoreException {
ILaunchManager manager = getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfiguration[] configurations = manager.getLaunchConfigurations(type);
for (int i = 0; i < configurations.length; i++) {
ILaunchConfiguration configuration = configurations[i];
if (configuration.getName().equals(launchConfigurationName)) {
configuration.delete();
break;
}
}
}
private ILaunch saveAndLaunchConfig() {
ILaunchConfiguration configuration = null;
ILaunch result = null;
try {
configuration = this.launchConfig.doSave();
}
catch (CoreException saveException) {
throw new RuntimeException("Could not save LaunchConfig", saveException);
}
try {
result = configuration.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor());
}
catch (CoreException lauchException) {
throw new RuntimeException("An error occured during launch", lauchException);
}
return result;
}
private void addLaunchListener() {
this.getLaunchManager().addLaunchListener(this.buildLaunchListener());
}
private ILaunchesListener2 buildLaunchListener() {
return new ILaunchesListener2() {
public void launchesTerminated(ILaunch[] launches) {
for (int i = 0; i < launches.length; i++) {
ILaunch launch = launches[i];
if (launch.equals(SchemaGenerator.this.getLaunch())) {
SchemaGenerator.this.postGenerate();
return;
}
}
}
public void launchesAdded(ILaunch[] launches) {
// not interested to this event
}
public void launchesChanged(ILaunch[] launches) {
// not interested to this event
}
public void launchesRemoved(ILaunch[] launches) {
// not interested to this event
}
};
}
// ********** Queries **********
private IRuntimeClasspathEntry getSystemLibraryClasspathEntry() throws CoreException {
IPath systemLibsPath = new Path(JavaRuntime.JRE_CONTAINER);
return JavaRuntime.newRuntimeContainerClasspathEntry(systemLibsPath, IRuntimeClasspathEntry.STANDARD_CLASSES);
}
private IRuntimeClasspathEntry getArchiveClasspathEntry(IPath archivePath) {
IRuntimeClasspathEntry archiveEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(archivePath);
archiveEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES);
return archiveEntry;
}
private IRuntimeClasspathEntry getDefaultProjectClasspathEntry(IJavaProject project) {
IRuntimeClasspathEntry projectEntry = JavaRuntime.newDefaultProjectClasspathEntry(project);
projectEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES);
return projectEntry;
}
public IProject getProject() {
return this.javaProject.getProject();
}
private IVMInstall getProjectJRE() throws CoreException {
return JavaRuntime.getVMInstall(this.javaProject);
}
protected ILaunch getLaunch() {
return this.launch;
}
protected ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
// ********** private method **********
private IPath buildBootstrapJarPath(String jarName) {
try {
File jarInstallDir = this.getBundleParentDir(JptJaxbUiPlugin.PLUGIN_ID);
List<File> result = new ArrayList<File>();
this.findFile(jarName, jarInstallDir, result);
if (result.isEmpty()) {
throw new RuntimeException("Could not find: " + jarName + "#.#.#v###.jar in: " + jarInstallDir); //$NON-NLS-1$ //$NON-NLS-2$
}
File ddlGenJarFile = result.get(0);
String ddlGenJarPath = ddlGenJarFile.getCanonicalPath();
return new Path(ddlGenJarPath);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private void findFile(String fileName, File directory, List<? super File> list) {
if(directory.listFiles() == null) {
throw new RuntimeException("Could not find directory: " + directory); //$NON-NLS-1$
}
for (File file : directory.listFiles()) {
if (file.getName().startsWith(fileName)) {
list.add(file);
}
if (file.isDirectory()) {
this.findFile(fileName, file, list);
}
}
}
private File getBundleParentDir(String bundleName) throws IOException {
if (Platform.inDevelopmentMode()) {
Location eclipseHomeLoc = Platform.getInstallLocation();
String eclipseHome = eclipseHomeLoc.getURL().getPath();
if ( ! eclipseHome.endsWith(PLUGINS_DIR)) {
eclipseHome += PLUGINS_DIR;
}
return new File(eclipseHome);
}
Bundle bundle = Platform.getBundle(bundleName);
return FileLocator.getBundleFile(bundle).getParentFile();
}
}