Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMilos Kleint2011-01-02 04:00:53 -0500
committerMilos Kleint2011-01-02 04:00:53 -0500
commitd0e05d10a7071f4e97c4d15bbb8b5cf284c869ee (patch)
tree9d14e39bf0b7768f8fe891b3b1dc6bcf00bf8460
parent0022fb6be37883070b2fb9a24a1155258241ccad (diff)
downloadm2e-core-d0e05d10a7071f4e97c4d15bbb8b5cf284c869ee.tar.gz
m2e-core-d0e05d10a7071f4e97c4d15bbb8b5cf284c869ee.tar.xz
m2e-core-d0e05d10a7071f4e97c4d15bbb8b5cf284c869ee.zip
MNGECLIPSE-2690 create new framework for doing large scale edits of multiple pom files. using wst's IDOMModel. solves problems with long undo chains, shall be reusable for both in-editor and outside-of-editor changes.
-rw-r--r--org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/composites/DependenciesComposite.java2
-rw-r--r--org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/dialogs/ManageDependenciesDialog.java156
-rw-r--r--org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/internal/PomEdits.java202
3 files changed, 277 insertions, 83 deletions
diff --git a/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/composites/DependenciesComposite.java b/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/composites/DependenciesComposite.java
index 83ba71df..42eb4615 100644
--- a/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/composites/DependenciesComposite.java
+++ b/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/composites/DependenciesComposite.java
@@ -697,7 +697,7 @@ public class DependenciesComposite extends Composite {
}
final ManageDependenciesDialog manageDepDialog = new ManageDependenciesDialog(getShell(), model, hierarchy,
- pomEditor.getEditingDomain(), dependenciesEditor.getSelection());
+ dependenciesEditor.getSelection());
manageDepDialog.open();
}
diff --git a/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/dialogs/ManageDependenciesDialog.java b/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/dialogs/ManageDependenciesDialog.java
index 4781492f..6ea84a02 100644
--- a/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/dialogs/ManageDependenciesDialog.java
+++ b/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/dialogs/ManageDependenciesDialog.java
@@ -43,6 +43,7 @@ import org.eclipse.m2e.core.ui.dialogs.AbstractMavenDialog;
import org.eclipse.m2e.editor.MavenEditorPlugin;
import org.eclipse.m2e.editor.composites.DependencyLabelProvider;
import org.eclipse.m2e.editor.composites.ListEditorContentProvider;
+import org.eclipse.m2e.editor.internal.PomEdits;
import org.eclipse.m2e.model.edit.pom.Dependency;
import org.eclipse.m2e.model.edit.pom.DependencyManagement;
import org.eclipse.m2e.model.edit.pom.Model;
@@ -66,6 +67,11 @@ import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Tree;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
/**
@@ -86,8 +92,6 @@ public class ManageDependenciesDialog extends AbstractMavenDialog {
LinkedList<MavenProject> projectHierarchy;
- final protected EditingDomain editingDomain;
-
private IStatus status;
private List<Object> originalSelection;
@@ -96,13 +100,12 @@ public class ManageDependenciesDialog extends AbstractMavenDialog {
* Hierarchy is a LinkedList representing the hierarchy relationship between POM represented by model and its parents.
* The head of the list should be the child, while the tail should be the root parent, with the others in between.
*/
- public ManageDependenciesDialog(Shell parent, Model model, LinkedList<MavenProject> hierarchy,
- EditingDomain editingDomain) {
- this(parent, model, hierarchy, editingDomain, null);
+ public ManageDependenciesDialog(Shell parent, Model model, LinkedList<MavenProject> hierarchy) {
+ this(parent, model, hierarchy, null);
}
public ManageDependenciesDialog(Shell parent, Model model, LinkedList<MavenProject> hierarchy,
- EditingDomain editingDomain, List<Object> selection) {
+ List<Object> selection) {
super(parent, DIALOG_SETTINGS);
setShellStyle(getShellStyle() | SWT.RESIZE);
@@ -110,7 +113,6 @@ public class ManageDependenciesDialog extends AbstractMavenDialog {
this.model = model;
this.projectHierarchy = hierarchy;
- this.editingDomain = editingDomain;
this.originalSelection = selection;
}
@@ -234,89 +236,79 @@ public class ManageDependenciesDialog extends AbstractMavenDialog {
*/
protected void computeResult() {
MavenProject targetPOM = getTargetPOM();
- IMavenProjectFacade facade = MavenPlugin.getDefault().getMavenProjectManager()
+ IMavenProjectFacade targetFacade = MavenPlugin.getDefault().getMavenProjectManager()
.getMavenProject(targetPOM.getGroupId(), targetPOM.getArtifactId(), targetPOM.getVersion());
+ MavenProject currentPOM = projectHierarchy.getFirst();
+ IMavenProjectFacade currentFacade = MavenPlugin.getDefault().getMavenProjectManager()
+ .getMavenProject(currentPOM.getGroupId(), currentPOM.getArtifactId(), currentPOM.getVersion());
- /*
- * Load the target model so we can make modifications to it
- */
- Model targetModel = null;
- if(targetPOM.equals(getProjectHierarchy().getFirst())) {
- //Editing the same models in both cases
- targetModel = model;
- } else {
- targetModel = loadTargetModel(facade);
- if(targetModel == null) {
- return;
- }
- }
-
- LinkedList<Dependency> dependencies = getDependenciesList();
-
- /*
- * 1) Remove version values from the dependencies from the current POM
- * 2) Add dependencies to dependencyManagement of targetPOM
- */
-
- CompoundCommand command = new CompoundCommand();
-
- //First we remove the version from the original dependency
- for(Dependency dep : dependencies) {
- Command unset = SetCommand.create(editingDomain, dep, PomPackage.eINSTANCE.getDependency_Version(),
- SetCommand.UNSET_VALUE);
- command.append(unset);
+ if (targetFacade == null || currentFacade == null) {
+ return;
}
+ boolean same = targetPOM.equals(currentPOM);
- DependencyManagement management = targetModel.getDependencyManagement();
- if(management == null) {
- //Add dependency management element if it does not exist
- management = PomFactory.eINSTANCE.createDependencyManagement();
- Command createDepManagement = SetCommand.create(editingDomain, targetModel,
- PomPackage.eINSTANCE.getModel_DependencyManagement(), management);
- command.append(createDepManagement);
- } else {
- //Filter out of the list of dependencies for which we need new entries in the dependency management section.
- for(Dependency depFromTarget : management.getDependencies()) {
- Iterator<Dependency> iter = dependencies.iterator();
- while(iter.hasNext()) {
- Dependency depFromSource = iter.next();
- if(depFromSource.getGroupId().equals(depFromTarget.getGroupId())
- && depFromSource.getArtifactId().equals(depFromTarget.getArtifactId())) {
- /*
- * Dependency already exists in the target's dependencyManagement,
- * so we don't need to add it.
- */
- iter.remove();
- //TODO: mkleint: what if the existing managed version differs from the version in the child pom?
+ final LinkedList<Dependency> modelDeps = getDependenciesList();
+
+ PomEdits.Operation removeVersionsOperation = new PomEdits.Operation() {
+ public void process(Document document) {
+ //we assume <dependencies> element exists here..
+ List<Element> dependencies = PomEdits.findDependencies(document.getDocumentElement());
+ assert dependencies != null;
+ for (Element dep : dependencies) {
+ String artifactId = PomEdits.getTextValue(PomEdits.findChild(dep, "artifactId"));
+ String groupId = PomEdits.getTextValue(PomEdits.findChild(dep, "groupId"));
+ //TODO: mkleint: nested cycles are ugly..
+ for (Dependency md : modelDeps) {
+ if (artifactId.equals(md.getArtifactId()) && groupId.equals(md.getGroupId())) {
+ PomEdits.removeChild(dep, "version");
+ break;
+ }
}
}
}
- }
-
- //Add new entry in dependency mgt section
- for(Dependency dep : dependencies) {
- Dependency clone = PomFactory.eINSTANCE.createDependency();
- clone.setGroupId(dep.getGroupId());
- clone.setArtifactId(dep.getArtifactId());
- clone.setVersion(dep.getVersion());
-
- Command addDepCommand = AddCommand.create(editingDomain, management,
- PomPackage.eINSTANCE.getDependencyManagement_Dependencies(), clone);
-
- command.append(addDepCommand);
- }
- editingDomain.getCommandStack().execute(command);
-
- }
-
- protected Model loadTargetModel(IMavenProjectFacade facade) {
+ };
+
+ PomEdits.Operation manageOperation = new PomEdits.Operation() {
+ public void process(Document document) {
+ Element mands = PomEdits.getManagedDependencies(document.getDocumentElement());
+ List<Dependency> modelDependencies = new ArrayList<Dependency>(modelDeps);
+ List<Element> existing = PomEdits.findChilds(mands, "dependency");
+ if (existing != null) {
+ for (Element dep : existing) {
+ String artifactId = PomEdits.getTextValue(PomEdits.findChild(dep, "artifactId"));
+ String groupId = PomEdits.getTextValue(PomEdits.findChild(dep, "groupId"));
+ //TODO: mkleint: nested cycles are ugly..
+ //clone list, shall not modify shared resource (used by the remove operation)
+ Iterator<Dependency> mdIter = modelDependencies.iterator();
+ while (mdIter.hasNext()) {
+ //TODO: here we iterate to find existing managed dependencies and decide not to overwrite them.
+ // but this could eventually break the current project when the versions are diametrally different
+ // we should have shown this information to the user in the UI in the first place (for him to decide what to do)
+ Dependency md = mdIter.next();
+ if (artifactId.equals(md.getArtifactId()) && groupId.equals(md.getGroupId())) {
+ mdIter.remove();
+ break;
+ }
+ }
+ }
+ }
+ for (Dependency md : modelDependencies) {
+ PomEdits.createDependency(mands, md.getGroupId(), md.getArtifactId(), md.getVersion());
+ }
+ }
+ };
+
try {
- PomResourceImpl resource = MavenPlugin.getDefault().getMavenModelManager().loadResource(facade.getPom());
- resource.load(Collections.EMPTY_MAP);
- return resource.getModel();
- } catch(Exception e) {
- MavenLogger.log("Can't load model " + facade.getPomFile().getPath(), e); //$NON-NLS-1$
- return null;
+ if (same) {
+ PomEdits.performOnDOMDocument(currentFacade.getPom(), new PomEdits.CompoundOperation(manageOperation, removeVersionsOperation));
+ } else {
+ PomEdits.performOnDOMDocument(targetFacade.getPom(), manageOperation);
+ PomEdits.performOnDOMDocument(currentFacade.getPom(), removeVersionsOperation);
+ }
+ } catch(IOException e) {
+ MavenLogger.log("", e);
+ } catch(CoreException e) {
+ MavenLogger.log(e);
}
}
diff --git a/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/internal/PomEdits.java b/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/internal/PomEdits.java
new file mode 100644
index 00000000..6845a022
--- /dev/null
+++ b/org.eclipse.m2e.editor/src/org/eclipse/m2e/editor/internal/PomEdits.java
@@ -0,0 +1,202 @@
+package org.eclipse.m2e.editor.internal;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.m2e.core.internal.project.MavenMarkerManager;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.format.IStructuredFormatProcessor;
+import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.core.internal.provisional.format.FormatProcessorXML;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * this class contains tools for editing the pom files using dom tree operations.
+ * @author mkleint
+ *
+ */
+public class PomEdits {
+
+
+ public static Element findChild(Element parent, String name) {
+ return MavenMarkerManager.findChildElement(parent, name);
+ }
+
+ public static List<Element> findChilds(Element parent, String name) {
+ return MavenMarkerManager.findChildElements(parent, name);
+ }
+
+ public static String getTextValue(Node element) {
+ return MavenMarkerManager.getElementTextValue(element);
+ }
+
+ /**
+ * node is expected to be the node containing <dependencies> node, so <project>, <dependencyManagement> etc..
+ * @param node
+ * @return
+ */
+ public static List<Element> findDependencies(Element node) {
+ return findChilds(findChild(node, "dependencies"), "dependency");
+ }
+
+ /** for the root <project> node (or equivalent) finds or creates the <dm> and <dependencies> sections.
+ * returns the <dependencies> section element.
+ *
+ * @param root
+ * @return
+ */
+ public static Element getManagedDependencies(Element root) {
+ Element toRet = getChild(root, "dependencyManagement");
+ toRet = getChild(toRet, "dependencies");
+ return toRet;
+ }
+
+ /**
+ * creates and adds new dependency to the parent.
+ * @param parentList
+ * @param groupId null or value
+ * @param artifactId never null
+ * @param version null or value
+ * @return
+ */
+ public static Element createDependency(Element parentList, String groupId, String artifactId, String version) {
+ Document doc = parentList.getOwnerDocument();
+ Element dep = doc.createElement("dependency");
+ parentList.appendChild(dep);
+
+ if (groupId != null) {
+ Element grid = doc.createElement("groupId");
+ dep.appendChild(grid);
+ grid.appendChild(doc.createTextNode(groupId));
+ }
+ Element artid = doc.createElement("artifactId");
+ dep.appendChild(artid);
+ artid.appendChild(doc.createTextNode(artifactId));
+ if (version != null) {
+ Element vers = doc.createElement("version");
+ dep.appendChild(vers);
+ vers.appendChild(doc.createTextNode(version));
+ }
+ format(dep);
+ return dep;
+ }
+
+ /**
+ * unlike the findChild() equivalent, this one creates the element if not present and returns it.
+ * Therefore it shall only be invoked within the PomEdits.Operation
+ * @param parent
+ * @param name
+ * @return
+ */
+ public static Element getChild(Element parent, String name) {
+ Element toRet = findChild(parent, name);
+ if (toRet == null) {
+ toRet = parent.getOwnerDocument().createElement(name);
+ parent.appendChild(toRet);
+ format(toRet);
+ }
+ return toRet;
+ }
+
+ /**
+ * proper remove of a child element
+ * @param parent
+ * @param name
+ */
+ public static void removeChild(Element parent, String name) {
+ Element child = PomEdits.findChild(parent, name);
+ if (child != null) {
+ Node prev = child.getPreviousSibling();
+ if (prev instanceof Text) {
+ Text txt = (Text)prev;
+ int lastnewline = txt.getData().lastIndexOf("\n");
+ txt.setData(txt.getData().substring(0, lastnewline));
+ }
+ parent.removeChild(child);
+ }
+ }
+
+ /**
+ * formats the node (and content). please make sure to only format the node you have created..
+ * @param newNode
+ */
+ public static void format(Node newNode) {
+ if (newNode.getParentNode() != null && newNode.equals(newNode.getParentNode().getLastChild())) {
+ //add a new line to get the newly generated content correctly formatted.
+ newNode.getParentNode().appendChild(newNode.getParentNode().getOwnerDocument().createTextNode("\n"));
+ }
+ IStructuredFormatProcessor formatProcessor = new FormatProcessorXML();
+ formatProcessor.formatNode(newNode);
+ }
+
+ /**
+ * performs an modifying operation on top the
+ * @param file
+ * @param operation
+ * @throws IOException
+ * @throws CoreException
+ */
+ public static void performOnDOMDocument(IFile file, PomEdits.Operation operation) throws IOException, CoreException {
+ assert file != null;
+ assert operation != null;
+ IDOMModel domModel = null;
+ //TODO we might want to attempt iterating opened editors and somehow initialize those
+ // that were not yet initialized. Then we could avoid saving a file that is actually opened, but was never used so far (after restart)
+ try {
+ domModel = (IDOMModel) StructuredModelManager.getModelManager().getModelForEdit(file);
+ domModel.aboutToChangeModel();
+ IStructuredTextUndoManager undo = domModel.getStructuredDocument().getUndoManager();
+ undo.beginRecording(domModel);
+ try {
+ operation.process(domModel.getDocument());
+ } finally {
+ undo.endRecording(domModel);
+ domModel.changedModel();
+ }
+ } finally {
+ if (domModel != null) {
+ //saving shall only happen when the model is not held elsewhere (eg. in opened view)
+ if (domModel.isSaveNeeded() && domModel.getReferenceCountForEdit() == 1) {
+ domModel.save();
+ }
+ domModel.releaseFromEdit();
+ }
+ }
+ }
+
+ /**
+ * operation to perform on top of the DOM document. see performOnDOMDocument()
+ * @author mkleint
+ *
+ */
+ public static interface Operation {
+ void process(Document document);
+ }
+
+ /**
+ * an Operation instance that aggregates multiple operations and performs then in given order.
+ * @author mkleint
+ *
+ */
+ public static final class CompoundOperation implements Operation {
+
+ private final Operation[] operations;
+
+ public CompoundOperation(Operation... operations) {
+ this.operations = operations;
+ }
+
+ public void process(Document document) {
+ for (Operation oper : operations) {
+ oper.process(document);
+ }
+ }
+
+ }
+}

Back to the top