377790 - Add action on class to "Add to persistence.xml"
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/persistence/PersistenceUnit.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/persistence/PersistenceUnit.java
index c0b248c..929eec7 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/persistence/PersistenceUnit.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/persistence/PersistenceUnit.java
@@ -827,6 +827,11 @@
*/
int findInsertLocationForMappingFileRef();
+ /**
+ * Add the given list of class names to the persistence.xml excluding
+ * those that already exist.
+ */
+ void addClasses(Iterable<String> classNames, IProgressMonitor monitor);
// ********** validation **********
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/persistence/AbstractPersistenceUnit.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/persistence/AbstractPersistenceUnit.java
index 7d2742c..714e219 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/persistence/AbstractPersistenceUnit.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/persistence/AbstractPersistenceUnit.java
@@ -2181,6 +2181,34 @@
}
throw new IllegalArgumentException("Illegal type mapping key: " + key); //$NON-NLS-1$
}
+
+
+ // ********** add classes to persistence unit **********
+
+ public void addClasses(Iterable<String> classNames, IProgressMonitor monitor) {
+ SubMonitor sm = SubMonitor.convert(monitor, IterableTools.size(classNames));
+ for (String className : classNames) {
+ if(!classRefExists(className)) {
+ this.addSpecifiedClassRef(className);
+ }
+ sm.worked(1);
+ }
+ if (sm.isCanceled()) {
+ return;
+ }
+
+ this.getXmlPersistenceUnit().sortClasses();
+ sm.worked(1);
+ }
+
+ private boolean classRefExists(String className) {
+ for (ClassRef classRef : this.getSpecifiedClassRefs()) {
+ if( classRef.getClassName().equals(className)) {
+ return true;
+ }
+ }
+ return false;
+ }
// ********** misc **********
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.properties b/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.properties
index e8ce1ce..5804085 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.properties
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.properties
@@ -62,6 +62,7 @@
synchronizeClasses = Synchronize Class List
makePersistent = Make Persistent...
persistenceEditor = Persistence XML Editor
+addToPersistenceUnit = Add to Persistence Unit
javaPersistenceNode = Java Persistence
jpaNode = JPA
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.xml b/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.xml
index 231c989..707b64f 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.xml
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/plugin.xml
@@ -360,6 +360,11 @@
categoryId="org.eclipse.jpt.jpa.ui.jpaMetadataConversionCommands">
</command>
+ <command
+ id="org.eclipse.jpt.jpa.ui.addToPersistenceUnit"
+ name="%addToPersistenceUnit">
+ </command>
+
</extension>
@@ -636,6 +641,27 @@
</activeWhen>
</handler>
+ <handler
+ commandId="org.eclipse.jpt.jpa.ui.addToPersistenceUnit"
+ class="org.eclipse.jpt.jpa.ui.internal.handlers.AddToPersistenceUnitHandler">
+ <enabledWhen>
+ <with variable="selection">
+ <count value="+"/>
+ <iterate>
+ <and>
+ <adapt type="org.eclipse.jdt.core.IJavaElement" />
+ <or>
+ <instanceof value="org.eclipse.jdt.core.ICompilationUnit"/>
+ <instanceof value="org.eclipse.jdt.core.IType"/>
+ <instanceof value="org.eclipse.jdt.core.IPackageFragment"/>
+ <instanceof value="org.eclipse.jdt.core.IPackageFragmentRoot"/>
+ </or>
+ </and>
+ </iterate>
+ </with>
+ </enabledWhen>
+ </handler>
+
</extension>
@@ -828,6 +854,12 @@
<visibleWhen checkEnabled="true"/>
</command>
</menuContribution>
+
+ <menuContribution locationURI="popup:org.eclipse.jpt.jpa.ui.menu.JpaTools">
+ <command commandId="org.eclipse.jpt.jpa.ui.addToPersistenceUnit">
+ <visibleWhen checkEnabled="true"/>
+ </command>
+ </menuContribution>
<!-- contributions to the project configure menu -->
<menuContribution locationURI="popup:org.eclipse.ui.projectConfigure?after=additions">
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/property_files/jpt_jpa_ui.properties b/jpa/plugins/org.eclipse.jpt.jpa.ui/property_files/jpt_jpa_ui.properties
index 2d371a6..50c6b0c 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/property_files/jpt_jpa_ui.properties
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/property_files/jpt_jpa_ui.properties
@@ -12,6 +12,10 @@
ACCESS_TYPE_COMPOSITE_ACCESS=Access:
+ADD_CLASSES_TO_PERSISTENCE_UNIT_MESSAGE_TEXT=No persistence.xml found in the project
+ADD_CLASSES_TO_PERSISTENCE_UNIT_MESSAGE_TITLE=Add Classes to Persistence Unit
+ADD_CLASSES_TO_PERSISTENCE_UNIT_TASK_NAME=Adding classes to persistence unit...
+
ADD_TO_EAR_COMPOSITE_EAR_MEMBER_SHIP=EAR membership
ADD_TO_EAR_COMPOSITE_ADD_TO_EAR_LABEL=&Add project to an EAR
ADD_TO_EAR_COMPOSITE_EAR_PROJECT_LABEL=EAR pr&oject name:
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/JptJpaUiMessages.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/JptJpaUiMessages.java
index c85addc..dafe0ea 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/JptJpaUiMessages.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/JptJpaUiMessages.java
@@ -23,6 +23,10 @@
}
public static String ACCESS_TYPE_COMPOSITE_ACCESS;
+ public static String ADD_CLASSES_TO_PERSISTENCE_UNIT_MESSAGE_TEXT;
+ public static String ADD_CLASSES_TO_PERSISTENCE_UNIT_MESSAGE_TITLE;
+ public static String ADD_CLASSES_TO_PERSISTENCE_UNIT_TASK_NAME;
+
public static String ADD_TO_EAR_COMPOSITE_EAR_MEMBER_SHIP;
public static String ADD_TO_EAR_COMPOSITE_ADD_TO_EAR_LABEL;
public static String ADD_TO_EAR_COMPOSITE_EAR_PROJECT_LABEL;
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/handlers/AddToPersistenceUnitHandler.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/handlers/AddToPersistenceUnitHandler.java
new file mode 100644
index 0000000..1a7455e
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/handlers/AddToPersistenceUnitHandler.java
@@ -0,0 +1,346 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.ui.internal.handlers;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jpt.common.core.resource.xml.JptXmlResource;
+import org.eclipse.jpt.common.ui.internal.swt.widgets.DisplayTools;
+import org.eclipse.jpt.common.ui.internal.utility.SynchronousUiCommandContext;
+import org.eclipse.jpt.common.utility.command.Command;
+import org.eclipse.jpt.common.utility.internal.ObjectTools;
+import org.eclipse.jpt.common.utility.internal.iterable.TransformationIterable;
+import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter;
+import org.eclipse.jpt.jpa.core.JpaProject;
+import org.eclipse.jpt.jpa.core.JpaProjectManager;
+import org.eclipse.jpt.jpa.core.context.persistence.Persistence;
+import org.eclipse.jpt.jpa.core.context.persistence.PersistenceUnit;
+import org.eclipse.jpt.jpa.core.context.persistence.PersistenceXml;
+import org.eclipse.jpt.jpa.ui.JptJpaUiMessages;
+import org.eclipse.jpt.jpa.ui.internal.plugin.JptJpaUiPlugin;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public class AddToPersistenceUnitHandler extends AbstractHandler
+{
+ private IFile persistenceXmlFile;
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ ISelection selection = HandlerUtil.getCurrentSelectionChecked(event);
+
+ for (Map.Entry<IProject, Set<IType>> entry : this.buildSelectedTypes(selection).entrySet()) {
+ IProject project = entry.getKey();
+ Set<IType> types = entry.getValue();
+ JpaProject jpaProject = (JpaProject) project.getAdapter(JpaProject.class);
+ if (jpaProject != null) {
+ try {
+ PersistenceXml persistenceXml = jpaProject.getContextRoot().getPersistenceXml();
+ if (persistenceXml == null) {
+ MessageDialog.openInformation(DisplayTools.getShell(), JptJpaUiMessages.ADD_CLASSES_TO_PERSISTENCE_UNIT_MESSAGE_TITLE, JptJpaUiMessages.ADD_CLASSES_TO_PERSISTENCE_UNIT_MESSAGE_TEXT);
+ } else {
+ persistenceXmlFile = (IFile) persistenceXml.getResource();
+ IRunnableWithProgress runnable = new AddClassesRunnable(persistenceXmlFile, convertToTypeNames(types));
+ this.buildProgressMonitorDialog().run(true, true, runnable); // true => fork; true => cancellable
+ }
+ } catch (InvocationTargetException ex) {
+ JptJpaUiPlugin.instance().logError(ex);
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ JptJpaUiPlugin.instance().logError(ex);
+ }
+ }
+ }
+ return null;
+ }
+
+ protected Iterable<String> convertToTypeNames(Iterable<IType> types) {
+ return new TransformationIterable<IType, String>(types, new NameTransformer());
+ }
+
+ /* CU private */ class NameTransformer
+ extends TransformerAdapter<IType, String> {
+ @Override
+ public String transform(IType type) {
+ return type.getFullyQualifiedName('$');
+ }
+ }
+
+ // ********** selected types **********
+
+ /**
+ * Return a map containing lists of types, keyed by project.
+ * <p>
+ * The action is contributed for:<ul>
+ * <li>{@link IType}
+ * <li>{@link ICompilationUnit}
+ * <li>{@link IPackageFragment}
+ * <li>{@link IPackageFragmentRoot} that is a source folder
+ * </ul>
+ */
+ private Map<IProject, Set<IType>> buildSelectedTypes(ISelection currentSelection) {
+ if ( ! (currentSelection instanceof StructuredSelection)) {
+ return Collections.emptyMap();
+ }
+ HashMap<IProject, Set<IType>> types = new HashMap<IProject, Set<IType>>();
+ for (Object sel : ((StructuredSelection) currentSelection).toList()) {
+ switch (((IJavaElement) sel).getElementType()) {
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT :
+ this.addSelectedTypes((IPackageFragmentRoot) sel, types);
+ break;
+ case IJavaElement.PACKAGE_FRAGMENT :
+ this.addSelectedTypes((IPackageFragment) sel, types);
+ break;
+ case IJavaElement.COMPILATION_UNIT :
+ this.addSelectedTypes((ICompilationUnit) sel, types);
+ break;
+ case IJavaElement.TYPE :
+ this.addSelectedType((IType) sel, types);
+ break;
+ default :
+ break;
+ }
+ }
+ return types;
+ }
+
+ private void addSelectedTypes(IPackageFragmentRoot packageFragmentRoot, Map<IProject, Set<IType>> types) {
+ for (IJavaElement pkgFragment : this.getPackageFragments(packageFragmentRoot)) {
+ this.addSelectedTypes((IPackageFragment) pkgFragment, types);
+ }
+ }
+
+ private void addSelectedTypes(IPackageFragment packageFragment, Map<IProject, Set<IType>> types) {
+ for (ICompilationUnit compUnit : this.getCompilationUnits(packageFragment)) {
+ this.addSelectedTypes(compUnit, types);
+ }
+ }
+
+ private void addSelectedTypes(ICompilationUnit compilationUnit, Map<IProject, Set<IType>> types) {
+ IType primaryType = compilationUnit.findPrimaryType();
+ if (primaryType != null) {
+ this.addSelectedType(primaryType, types);
+ }
+ }
+
+ private void addSelectedType(IType primaryType, Map<IProject, Set<IType>> typesMap) {
+ IProject project = primaryType.getJavaProject().getProject();
+ Set<IType> types = typesMap.get(project);
+ if (types == null) {
+ types = new HashSet<IType>();
+ typesMap.put(project, types);
+ }
+ types.add(primaryType);
+ }
+
+ private ICompilationUnit[] getCompilationUnits(IPackageFragment packageFragment) {
+ try {
+ return packageFragment.getCompilationUnits();
+ }
+ catch (JavaModelException e) {
+ JptJpaUiPlugin.instance().logError(e);
+ }
+ return new ICompilationUnit[0];
+ }
+
+ private IJavaElement[] getPackageFragments(IPackageFragmentRoot packageFragmentRoot) {
+ try {
+ return packageFragmentRoot.getChildren();
+ }
+ catch (JavaModelException e) {
+ JptJpaUiPlugin.instance().logError(e);
+ }
+ return new IJavaElement[0];
+ }
+
+ private ProgressMonitorDialog buildProgressMonitorDialog() {
+ return new ProgressMonitorDialog(null);
+ }
+
+ // ********** add classes runnable **********
+
+ /**
+ * This is dispatched to the progress monitor dialog.
+ */
+ /* CU private */ static class AddClassesRunnable implements IRunnableWithProgress
+ {
+ private final IFile persistenceXmlFile;
+ private final Iterable<String> typeNames;
+
+ AddClassesRunnable(IFile persistenceXmlFile, Iterable<String> typeNames) {
+ super();
+ this.persistenceXmlFile = persistenceXmlFile;
+ this.typeNames = typeNames;
+ }
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ this.run_(monitor);
+ } catch (CoreException ex) {
+ throw new InvocationTargetException(ex);
+ }
+ }
+
+ private void run_(IProgressMonitor monitor) throws CoreException {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ // lock the entire project, since we might modify the metamodel classes
+ ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this.persistenceXmlFile.getProject());
+ workspace.run(
+ new AddClassesWorkspaceRunnable(this.persistenceXmlFile, typeNames),
+ rule,
+ IWorkspace.AVOID_UPDATE,
+ monitor
+ );
+ }
+ }
+
+
+ // ********** add classes workspace runnable **********
+
+ /**
+ * This is dispatched to the Eclipse workspace.
+ */
+ /* CU private */ static class AddClassesWorkspaceRunnable implements IWorkspaceRunnable
+ {
+ private final IFile persistenceXmlFile;
+ private final Iterable<String> typeNames;
+
+ AddClassesWorkspaceRunnable(IFile persistenceXmlFile, Iterable<String> typeNames) {
+ super();
+ this.persistenceXmlFile = persistenceXmlFile;
+ this.typeNames = typeNames;
+ }
+
+ public void run(IProgressMonitor monitor) throws CoreException {
+ if (monitor.isCanceled()) {
+ return;
+ }
+ SubMonitor sm = SubMonitor.convert(monitor, JptJpaUiMessages.ADD_CLASSES_TO_PERSISTENCE_UNIT_TASK_NAME, 20);
+
+ JpaProject jpaProject = this.getJpaProject();
+ if (jpaProject == null) {
+ return;
+ }
+
+ JptXmlResource resource = jpaProject.getPersistenceXmlResource();
+ if (resource == null) {
+ // the resource can be null if the persistence.xml file has an invalid content type
+ return;
+ }
+
+ if (sm.isCanceled()) {
+ return;
+ }
+ sm.worked(1);
+
+ PersistenceXml persistenceXml = jpaProject.getContextRoot().getPersistenceXml();
+ if (persistenceXml == null) {
+ return; // unlikely...
+ }
+
+ Persistence persistence = persistenceXml.getRoot();
+ if (persistence == null) {
+ return; // unlikely...
+ }
+
+ PersistenceUnit persistenceUnit = (persistence.getPersistenceUnitsSize() == 0) ?
+ persistence.addPersistenceUnit() :
+ persistence.getPersistenceUnit(0);
+ if (sm.isCanceled()) {
+ return;
+ }
+ sm.worked(1);
+
+ Command addClassesCommand = new AddClassesCommand(persistenceUnit, typeNames, sm.newChild(17));
+ JpaProjectManager mgr = this.getJpaProjectManager();
+ try {
+ if (mgr != null) {
+ mgr.execute(addClassesCommand, SynchronousUiCommandContext.instance());
+ }
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(ex);
+ }
+
+ resource.save();
+ sm.worked(1);
+ }
+
+ private JpaProjectManager getJpaProjectManager() {
+ return (JpaProjectManager) this.getWorkspace().getAdapter(JpaProjectManager.class);
+ }
+
+ private IProject getProject() {
+ return this.persistenceXmlFile.getProject();
+ }
+
+ private JpaProject getJpaProject() {
+ return (JpaProject) this.getProject().getAdapter(JpaProject.class);
+ }
+
+ private IWorkspace getWorkspace() {
+ return this.persistenceXmlFile.getWorkspace();
+ }
+ }
+
+
+ // ********** add classes command **********
+
+ /**
+ * This is dispatched to the JPA project manager.
+ */
+ /* CU private */ static class AddClassesCommand implements Command
+ {
+ private final PersistenceUnit persistenceUnit;
+ private final Iterable<String> typeNames;
+ private final IProgressMonitor monitor;
+
+ AddClassesCommand(PersistenceUnit persistenceUnit, Iterable<String> typeNames, IProgressMonitor monitor) {
+ super();
+ this.persistenceUnit = persistenceUnit;
+ this.typeNames = typeNames;
+ this.monitor = monitor;
+ }
+
+ public void execute() {
+ this.persistenceUnit.addClasses(typeNames, this.monitor);
+ }
+
+ @Override
+ public String toString() {
+ return ObjectTools.toString(this, this.persistenceUnit);
+ }
+ }
+}