/*************************************************************************************
* Copyright (c) 2014 Red Hat, Inc. and others.
* 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:
* JBoss by Red Hat - Initial implementation.
************************************************************************************/
package org.jboss.tools.arquillian.ui.internal.refactoring;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PROPERTIES;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.createElement;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.findChild;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.format;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.getChild;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.getTextValue;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.performOnDOMDocument;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.setText;
import static org.eclipse.m2e.core.ui.internal.editing.PomHelper.addOrUpdateDependency;
import java.util.ArrayList;
import java.util.List;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
import org.eclipse.compare.rangedifferencer.IRangeComparator;
import org.eclipse.compare.rangedifferencer.RangeDifference;
import org.eclipse.compare.rangedifferencer.RangeDifferencer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.DocumentChange;
import org.eclipse.ltk.core.refactoring.NullChange;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits.CompoundOperation;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits.OperationTuple;
import org.eclipse.m2e.core.ui.internal.editing.PomHelper;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.jboss.forge.arquillian.container.Container;
import org.jboss.tools.arquillian.core.ArquillianCoreActivator;
import org.jboss.tools.arquillian.core.internal.ArquillianConstants;
import org.jboss.tools.arquillian.core.internal.container.ContainerParser;
import org.jboss.tools.arquillian.core.internal.util.ArquillianUtility;
import org.jboss.tools.arquillian.ui.ArquillianUIActivator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* A refactoring to add Arquillian support to project
*
* @author snjeza
*
*/
public class AddArquillianSupportRefactoring extends Refactoring {
private IProject project;
private String version;
private boolean updatePom;
private boolean addProfiles;
private boolean updateBuild;
private boolean updateDependencies;
/**
* @param project
*/
public AddArquillianSupportRefactoring(IProject project) {
super();
this.project = project;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
*/
@Override
public String getName() {
return "Add Arquillian support";
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org
* .eclipse.core.runtime.IProgressMonitor)
*/
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
boolean isMavenProject = project != null
&& project.hasNature(IMavenConstants.NATURE_ID);
if (!isMavenProject) {
IStatus status = new Status(IStatus.ERROR,
ArquillianUIActivator.PLUGIN_ID,
"The project is not a valid maven project");
return RefactoringStatus.create(status);
}
return RefactoringStatus.create(Status.OK_STATUS);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(org
* .eclipse.core.runtime.IProgressMonitor)
*/
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
RefactoringStatus status = checkInitialConditions(pm);
if (!status.isOK()) {
return status;
}
if (isUpdatePom()) {
IFile file = getFile();
if (file == null || !file.exists()) {
IStatus s = new Status(IStatus.ERROR, ArquillianUIActivator.PLUGIN_ID,
"The pom.xml file does not exist");
return RefactoringStatus.create(s);
}
IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().create(project, new NullProgressMonitor());
if (facade == null) {
IStatus s = new Status(IStatus.ERROR, ArquillianUIActivator.PLUGIN_ID,
"The project is not a valid maven project");
return RefactoringStatus.create(s);
}
}
return status;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse
* .core.runtime.IProgressMonitor)
*/
@Override
public Change createChange(IProgressMonitor pm) throws CoreException,
OperationCanceledException {
if (!isUpdatePom()) {
return new NullChange();
}
IFile file = getFile();
IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().create(project, new NullProgressMonitor());
MavenProject mavenProject = facade.getMavenProject(new NullProgressMonitor());
String version = ArquillianUtility.getArquillianVersion(mavenProject);
if (version == null) {
List<Operation> operations = new ArrayList<Operation>();
Model model = ArquillianUtility.getArquilianModel(true);
if (isUpdateDependencies()) {
operations.add(new AddProperties(
ArquillianUtility.ARQUILLIAN_VERSION, getVersion()));
List<Dependency> dependencies = model.getDependencies();
operations.add(new AddDependencies(dependencies, mavenProject));
DependencyManagement dependencyMgmt = model.getDependencyManagement();
if (dependencyMgmt != null) {
dependencies = dependencyMgmt.getDependencies();
if (dependencies != null || dependencies.size() > 0) {
operations.add(new AddDependencyManagement(dependencies, mavenProject));
}
}
}
if (isUpdateBuild()) {
Build build = model.getBuild();
operations.add(new AddPlugins(build, file));
}
if (isAddProfiles()) {
operations.add(new AddProfiles(mavenProject));
}
CompoundOperation compound = new CompoundOperation(operations.toArray(new Operation[0]));
return createChange(file, compound, getName());
}
return new NullChange();
}
private Change createChange(IFile file, Operation operation, String label) throws CoreException {
IStructuredModel model = null;
try {
model = StructuredModelManager.getModelManager().getModelForRead(file);
IDocument document = model.getStructuredDocument();
boolean existing = isOpened(document);
IStructuredModel tempModel = StructuredModelManager.getModelManager().createUnManagedStructuredModelFor(
"org.eclipse.m2e.core.pomFile"); //$NON-NLS-1$
tempModel.getStructuredDocument().setText(StructuredModelManager.getModelManager(), document.get());
IDocument tempDocument = tempModel.getStructuredDocument();
performOnDOMDocument(new OperationTuple((IDOMModel) tempModel, operation));
TextChange change = createChange(existing ? null : file, document, tempDocument, label);
return change;
} catch (Exception e) {
ArquillianUIActivator.log(e);
throw new CoreException(new Status(IStatus.ERROR,
ArquillianUIActivator.PLUGIN_ID,
"An error occurred creating change", e));
} finally {
if (model != null) {
model.releaseFromRead();
}
}
}
private TextChange createChange(IFile oldFile, IDocument oldDocument,
IDocument newDocument, String label) {
TextChange change = oldFile == null ? new DocumentChange(label,
oldDocument) : new TextFileChange(label, oldFile);
MultiTextEdit textEdit = new MultiTextEdit();
change.setEdit(textEdit);
String newText = newDocument.get();
String oldText = oldDocument.get();
if (!newText.equals(oldText)) {
IRangeComparator right = new RangeComparator(oldText);
IRangeComparator left = new RangeComparator(newText);
RangeDifference[] differences = RangeDifferencer.findDifferences(right, left);
for (RangeDifference difference : differences) {
int rightStart = difference.rightStart();
int rightEnd = difference.rightEnd();
String text = newText.substring(rightStart, rightEnd);
int leftStart = difference.leftStart();
int leftLength = difference.leftLength();
textEdit.addChild(new ReplaceEdit(leftStart, leftLength, text));
}
}
return change;
}
private static boolean isOpened(IDocument document) {
for (IWorkbenchWindow window : PlatformUI.getWorkbench()
.getWorkbenchWindows()) {
for (IWorkbenchPage page : window.getPages()) {
for (IEditorReference ref : page.getEditorReferences()) {
IEditorPart editor = ref.getEditor(false);
if (editor != null) {
IDocument doc = (IDocument) editor
.getAdapter(IDocument.class);
if (doc != null && doc.equals(document)) {
return true;
}
}
}
}
}
return false;
}
private IFile getFile() throws CoreException {
if (project == null || !project.hasNature(IMavenConstants.NATURE_ID)) {
return null;
}
return project.getFile(IMavenConstants.POM_FILE_NAME);
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public boolean isUpdatePom() {
return updatePom;
}
public void setUpdatePom(boolean updatePom) {
this.updatePom = updatePom;
}
public boolean isAddProfiles() {
return addProfiles;
}
public void setAddProfiles(boolean addProfiles) {
this.addProfiles = addProfiles;
}
public IProject getProject() {
return project;
}
private static class AddProperties implements Operation {
private final String name;
private final String value;
public AddProperties(String name, String value) {
this.name = name;
this.value = value;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation#process
* (org.w3c.dom.Document)
*/
public void process(Document document) {
Element props = getChild(document.getDocumentElement(),
PROPERTIES);
Element existing = findChild(props, name);
if (existing == null) {
existing = document.createElement(name);
existing.appendChild(document.createTextNode(value));
props.appendChild(existing);
PomEdits.format(existing);
} else {
setText(existing, value);
}
}
}
private static class AddDependencyManagement implements Operation {
private List<Dependency> dependencies;
private MavenProject mavenProject;
public AddDependencyManagement(List<Dependency> dependencies, MavenProject mavenProject) {
this.dependencies = dependencies;
this.mavenProject = mavenProject;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation#process
* (org.w3c.dom.Document)
*/
public void process(Document document) {
if (dependencies == null || dependencies.size() <= 0) {
return;
}
List<Dependency> allDependencies = new ArrayList<Dependency>();
DependencyManagement pdMgmt = mavenProject.getDependencyManagement();
if (pdMgmt != null) {
List<Dependency> pmd = pdMgmt.getDependencies();
if (pmd != null) {
allDependencies.addAll(pmd);
}
}
Parent parent = mavenProject.getModel().getParent();
if (parent != null) {
try {
ArquillianUtility.addManagedDependencies(allDependencies, parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
} catch (CoreException e) {
ArquillianUIActivator.log(e);
}
}
Element root = document.getDocumentElement();
Element dependencyMgmtEl = getChild(root, PomEdits.DEPENDENCY_MANAGEMENT);
Element dependenciesEl = getChild(dependencyMgmtEl, PomEdits.DEPENDENCIES);
for (Dependency dependency:dependencies) {
if (!ArquillianUtility.managedDependencyExists(dependency, allDependencies)) {
addOrUpdateDependency(dependenciesEl,
dependency.getGroupId(), dependency.getArtifactId(),
dependency.getVersion(), dependency.getType(),
dependency.getScope(), dependency.getClassifier());
}
}
}
}
private static class AddPlugins implements Operation {
private Build build;
private IFile file;
public AddPlugins(Build build, IFile file) {
this.build = build;
this.file = file;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation#process
* (org.w3c.dom.Document)
*/
public void process(Document document) {
if (build == null) {
return;
}
List<Plugin> plugins = build.getPlugins();
Element root = document.getDocumentElement();
Element buildEl = getChild(root, PomEdits.BUILD);
Element pluginsEl = getChild(buildEl, PomEdits.PLUGINS);
IDOMModel model = null;
try {
model = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(file);
if (model == null) {
model = (IDOMModel) StructuredModelManager.getModelManager().getModelForRead(file);
}
for (Plugin plugin:plugins) {
if (!ArquillianUtility.pluginExists(model, plugin)) {
PomHelper.createPlugin(pluginsEl, plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion());
}
}
Element compiler = null;
List<Element> elements = PomEdits.findChilds(pluginsEl, PomEdits.PLUGIN);
for (Element element:elements) {
Element pluginArtifactId = findChild(element, PomEdits.ARTIFACT_ID);
String aid = getTextValue(pluginArtifactId);
Element pluginGroupId = findChild(element, PomEdits.GROUP_ID);
String gid = pluginGroupId == null ? ArquillianUtility.MAVEN_GROUP_ID : getTextValue(pluginGroupId);
if (ArquillianUtility.MAVEN_COMPILER_ARTIFACT_ID.equals(aid) && (ArquillianUtility.MAVEN_GROUP_ID.equals(gid))) {
compiler = element;
break;
}
}
ArquillianUtility.fixCompilerPlugin(compiler);
} catch (Exception e) {
ArquillianCoreActivator.log(e);
} finally {
if (model != null) {
model.releaseFromRead();
}
}
format(buildEl);
}
}
private static class AddProfiles implements Operation {
private MavenProject mavenProject;
public AddProfiles(MavenProject mavenProject) {
this.mavenProject = mavenProject;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation#process
* (org.w3c.dom.Document)
*/
public void process(Document document) {
List<String> selectedProfiles = ArquillianUtility.getProfilesFromPreferences(ArquillianConstants.SELECTED_ARQUILLIAN_PROFILES);
if (selectedProfiles == null || selectedProfiles.size() <= 0) {
return;
}
List<Container> selectedContainers = new ArrayList<Container>();
Model projectModel = mavenProject.getModel();
List<String> allProfiles;
try {
allProfiles = ArquillianUtility.getProfiles(projectModel);
} catch (CoreException e) {
ArquillianUIActivator.log(e);
return;
}
for(Container container:ContainerParser.getContainers()) {
if (selectedProfiles.contains(container.getId()) &&
!allProfiles.contains(container.getId())) {
selectedContainers.add(container);
}
}
if (selectedContainers.size() <= 0) {
return;
}
Element root = document.getDocumentElement();
Element profiles = getChild(root, PomEdits.PROFILES);
for (Container container:selectedContainers) {
//org.eclipse.m2e.model.edit.pom.Profile profile = ProfileGenerator.getProfile(container);
generateProfile(profiles, container);
}
}
private void generateProfile(Element profiles, Container container) {
if (container == null) {
return;
}
String id = container.getId();
Element profile = createElement(profiles, PomEdits.PROFILE);
Element idEl = getChild(profile, PomEdits.ID);
setText(idEl, id);
Element dependencies = getChild(profile, PomEdits.DEPENDENCIES);
String version = getVersion(container.getGroup_id(),
container.getArtifact_id());
PomHelper.addOrUpdateDependency(dependencies,
container.getGroup_id(), container.getArtifact_id(),
version, null, null, null);
List<org.jboss.forge.arquillian.container.Dependency> deps = container
.getDependencies();
if (deps != null) {
for (org.jboss.forge.arquillian.container.Dependency fd : deps) {
version = getVersion(fd.getGroup_id(), fd.getArtifact_id());
PomHelper.addOrUpdateDependency(dependencies,
fd.getGroup_id(), fd.getArtifact_id(), version,
null, null, null);
}
}
format(profile);
}
private String getVersion(String gid, String aid) {
String coords = gid + ":" + aid + ":[0,)"; //$NON-NLS-1$//$NON-NLS-2$
return ArquillianUtility.getHighestVersion(coords);
}
}
public boolean isUpdateBuild() {
return updateBuild;
}
public void setUpdateBuild(boolean updateBuild) {
this.updateBuild = updateBuild;
}
public boolean isUpdateDependencies() {
return updateDependencies;
}
public void setUpdateDependencies(boolean updateDependencies) {
this.updateDependencies = updateDependencies;
}
}