/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.jdt.refactoring.session;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ChangeCreationResult;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ChangeInfo;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RefactoringResult;
import org.eclipse.che.jdt.refactoring.DtoConverter;
import org.eclipse.che.jdt.refactoring.RefactoringException;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.MoveCompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.RenameCompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.RenamePackageChange;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CheckConditionsOperation;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.CreateChangeOperation;
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.UndoTextFileChange;
import org.eclipse.ltk.internal.core.refactoring.Messages;
import org.eclipse.ltk.internal.ui.refactoring.AbstractChangeNode;
import org.eclipse.ltk.internal.ui.refactoring.FinishResult;
import org.eclipse.ltk.internal.ui.refactoring.PreviewNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* The base class of the refactoring that describes all operation.
*
* @author Evgen Vidolob
* @author Valeriy Svydenko
*/
public abstract class RefactoringSession {
protected Refactoring refactoring;
protected PreviewNode previewNode;
private RefactoringStatus conditionCheckingStatus;
private Change change;
public RefactoringSession(Refactoring refactoring) {
this.refactoring = refactoring;
}
public ChangeCreationResult createChange() throws RefactoringException {
Change change = createChange(new CreateChangeOperation(
new CheckConditionsOperation(refactoring, CheckConditionsOperation.FINAL_CONDITIONS),
RefactoringStatus.FATAL), true);
// Status has been updated since we have passed true
RefactoringStatus status = conditionCheckingStatus;
// Creating the change has been canceled
if (change == null && status == null) {
internalSetChange(change);
throw new RefactoringException("Creating the change has been canceled");
}
// Set change if we don't have fatal errors.
if (!status.hasFatalError()) {
internalSetChange(change);
}
ChangeCreationResult result = DtoFactory.newDto(ChangeCreationResult.class);
result.setStatus(DtoConverter.toRefactoringStatusDto(status));
result.setCanShowPreviewPage(status.isOK());
return result;
}
private void internalSetChange(Change change) {
this.change = change;
}
private Change createChange(CreateChangeOperation operation, boolean updateStatus) throws RefactoringException {
CoreException exception = null;
try {
ResourcesPlugin.getWorkspace().run(operation, new NullProgressMonitor());
} catch (CoreException e) {
exception = e;
}
if (updateStatus) {
RefactoringStatus status = null;
if (exception != null) {
status = new RefactoringStatus();
String msg = exception.getMessage();
if (msg != null) {
status.addFatalError(Messages.format("{0}. See the error log for more details.", msg));
} else {
status.addFatalError(
"An unexpected exception occurred while creating a change object. See the error log for more details.");
}
JavaPlugin.log(exception);
} else {
status = operation.getConditionCheckingStatus();
}
setConditionCheckingStatus(status);
} else {
if (exception != null)
throw new RefactoringException(exception);
}
Change change = operation.getChange();
return change;
}
public void setConditionCheckingStatus(RefactoringStatus conditionCheckingStatus) {
this.conditionCheckingStatus = conditionCheckingStatus;
}
public Change getChange() {
return change;
}
/**
* @return instance of {@link org.eclipse.che.ide.ext.java.shared.dto.refactoring.RefactoringStatus}.
* That describes status of the refactoring operation.
*/
public RefactoringResult apply() {
PerformChangeOperation operation = new PerformChangeOperation(change);
FinishResult result = internalPerformFinish(operation);
if (result.isException()) {
return DtoConverter.toRefactoringResultDto(RefactoringStatus.createErrorStatus("Refactoring failed with Exception."));
}
CompositeChange operationChange = (CompositeChange)operation.getUndoChange();
Change[] changes = operationChange.getChildren();
RefactoringStatus validationStatus = operation.getValidationStatus();
if (validationStatus != null) {
List<ChangeInfo> changesInfo = new ArrayList<>();
prepareChangesInfo(changes, changesInfo);
RefactoringResult status = DtoConverter.toRefactoringResultDto(validationStatus);
status.setChanges(changesInfo);
return status;
}
return DtoConverter.toRefactoringResultDto(new RefactoringStatus());
}
/**
* Prepare the information about changes which were applied.
*
* @param changes array of the applied changes
* @param changesInfo prepared list of {@link ChangeInfo}
*/
public void prepareChangesInfo(Change[] changes, List<ChangeInfo> changesInfo) {
for (Change ch : changes) {
if (ch instanceof DynamicValidationStateChange) {
prepareChangesInfo(((DynamicValidationStateChange)ch).getChildren(), changesInfo);
} else {
ChangeInfo changeInfo = DtoFactory.newDto(ChangeInfo.class);
String refactoringName = ch.getName();
if (ch instanceof UndoTextFileChange) {
changeInfo.setName(ChangeInfo.ChangeName.UPDATE);
changeInfo.setPath(((CompilationUnit)ch.getModifiedElement()).getPath().toString());
}
if (refactoringName.startsWith("Rename")) {
if (ch instanceof RenameCompilationUnitChange) {
prepareRenameCompilationUnitChange(changeInfo, ch);
} else if (ch instanceof RenamePackageChange) {
prepareRenamePackageChange(changesInfo, changeInfo, ch);
}
}
if (refactoringName.startsWith("Move")) {
prepareMoveChange(changeInfo, ch);
}
changesInfo.add(changeInfo);
}
}
}
private void prepareMoveChange(ChangeInfo changeInfo, Change ch) {
changeInfo.setName(ChangeInfo.ChangeName.MOVE);
if (ch instanceof MoveCompilationUnitChange) {
MoveCompilationUnitChange moveChange = (MoveCompilationUnitChange)ch;
String className = moveChange.getCu().getPath().lastSegment();
changeInfo.setOldPath(moveChange.getDestinationPackage().getPath().append(className).toString());
changeInfo.setPath(((CompilationUnit)ch.getModifiedElement()).getPath().toString());
}
}
private void prepareRenamePackageChange(List<ChangeInfo> changesInfo, ChangeInfo changeInfo, Change ch) {
changeInfo.setName(ChangeInfo.ChangeName.RENAME_PACKAGE);
RenamePackageChange renameChange = (RenamePackageChange)ch;
IPath oldPackageName = new Path(renameChange.getOldName().replace('.', IPath.SEPARATOR));
IPath newPackageName = new Path(renameChange.getNewName().replace('.', IPath.SEPARATOR));
changeInfo.setOldPath(renameChange.getResourcePath()
.removeLastSegments(oldPackageName.segmentCount())
.append(newPackageName).toString());
changeInfo.setPath(renameChange.getResourcePath().toString());
Set<IResource> compilationUnits = renameChange.getFCompilationUnitStamps().keySet();
for (IResource iResource : compilationUnits) {
ChangeInfo change = DtoFactory.newDto(ChangeInfo.class);
change.setName(ChangeInfo.ChangeName.UPDATE);
IPath fullPathOldPath = iResource.getFullPath();
IPath newPath = renameChange.getResourcePath().append(fullPathOldPath.toFile().getName());
change.setOldPath(fullPathOldPath.toString());
change.setPath(newPath.toString());
changesInfo.add(change);
}
}
private void prepareRenameCompilationUnitChange(ChangeInfo changeInfo, Change ch) {
changeInfo.setName(ChangeInfo.ChangeName.RENAME_COMPILATION_UNIT);
changeInfo.setPath(((CompilationUnit)ch.getModifiedElement()).getPath().toString());
RenameCompilationUnitChange renameChange = (RenameCompilationUnitChange)ch;
changeInfo.setOldPath(renameChange.getResourcePath().removeLastSegments(1).append(renameChange.getNewName()).toString());
}
private FinishResult internalPerformFinish(PerformChangeOperation op) {
op.setUndoManager(RefactoringCore.getUndoManager(), refactoring.getName());
try {
ResourcesPlugin.getWorkspace().run(op, new NullProgressMonitor());
} catch (CoreException e) {
JavaPlugin.log(e);
return FinishResult.createException();
}
return FinishResult.createOK();
}
public PreviewNode getChangePreview() {
CompositeChange compositeChange;
if (change instanceof CompositeChange) {
compositeChange = (CompositeChange)change;
} else {
compositeChange = new CompositeChange("Dummy Change"); //$NON-NLS-1$
compositeChange.add(change);
}
previewNode = AbstractChangeNode.createNode(null, compositeChange);
return previewNode;
}
public void updateChangeEnabled(String changeId, boolean enabled) throws RefactoringException {
PreviewNode node = findNode(previewNode, changeId);
if (node == null) {
throw new RefactoringException("Can't find refactoring change to update enabled state.");
}
node.setEnabled(enabled);
}
private PreviewNode findNode(PreviewNode node, String id) {
if (node.getId().equals(id)) {
return node;
}
PreviewNode[] children = node.getChildren();
if (children != null) {
PreviewNode found;
for (PreviewNode child : children) {
found = findNode(child, id);
if (found != null) {
return found;
}
}
}
return null;
}
public PreviewNode getChangePreview(String changeId) {
return findNode(previewNode, changeId);
}
/**
* Disposes this refactoring session.
*/
public void dispose() {
if (change != null) {
change.dispose();
}
}
}