/******************************************************************************* * Copyright (c) 2012 VMWare, Inc. * 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: * VMWare, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.refactoring.rename.type; import static org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind.CONTROLLER_CLASS; import static org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind.DOMAIN_CLASS; import static org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind.INTEGRATION_TEST; import static org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind.SERVICE_CLASS; import static org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind.TAGLIB_CLASS; import static org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind.UNIT_TEST; import java.util.ArrayList; import java.util.Collection; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring; import org.eclipse.ltk.core.refactoring.participants.RenameParticipant; import org.grails.ide.eclipse.core.GrailsCoreActivator; import org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind; import org.grails.ide.eclipse.editor.groovy.elements.GrailsProject; import org.grails.ide.eclipse.editor.groovy.elements.GrailsWorkspaceCore; import org.grails.ide.eclipse.refactoring.rename.ParticipantChangeManager; import org.grails.ide.eclipse.refactoring.rename.ui.DialogBasedGrailsTypeRenameConfigurer; /** * @author Kris De Volder * @since 2.7 */ @SuppressWarnings("restriction") public final class GrailsTypeRenameParticipant extends RenameParticipant implements ITypeRenaming { final public static boolean DEBUG = false; void debug(String string) { if (DEBUG) { System.out.println(this.getClass().getSimpleName()+": " +string); } } /** * GrailsTypeRenameParticipant requires outside input to fully configure * what extra changes to perform. This interface abstracts away from how precisely the decision is being made. */ public interface IGrailsTypeRenameConfigurer { Collection<ITypeRenaming> chooseAdditionalRenamings(ITypeRenaming orgType, Collection<ITypeRenaming> values, RefactoringStatus status); boolean updateServiceReferences(); boolean updateGSPs(); } protected GrailsProject project; protected GroovyCompilationUnit cu; protected IType type; private Collection<ITypeRenaming> extraRenamings = null; private ArrayList<ExtraTypeRenamer> extraRenamers = null; private IGrailsTypeRenameConfigurer grailsRenameConfigurer = null; private ParticipantChangeManager changes = null; @Override protected boolean initialize(Object object) { if (object instanceof IType) { this.type = (IType) object; debug("initializing on type "+type.getElementName()); project = GrailsWorkspaceCore.get().getGrailsProjectFor(type); if (project!=null) { debug("project = "+project); IJavaElement parent = type.getParent(); if (parent instanceof GroovyCompilationUnit) { cu = (GroovyCompilationUnit) parent; debug("cu = "+cu); return isInteresting(); } } } debug("not applicable"); return false; } private IGrailsTypeRenameConfigurer getConfigurer() { if (grailsRenameConfigurer==null) { GrailsTypeRenameRefactoring refactoring = getGrailsTypeRefactoring(); if (refactoring!=null) { grailsRenameConfigurer = refactoring.getRenameConfigurer(); } else { grailsRenameConfigurer = new DialogBasedGrailsTypeRenameConfigurer(); } } return grailsRenameConfigurer; } /** * Subclass should implement this to specify whether the target element being renamed is interesting to the participant. * By the time this method is called, the fields project, cu and type fields should have been initialised. */ protected boolean isInteresting() { GrailsElementKind kind = project.getElementKind(cu); debug("kind = " + kind); return isInterestingKind(kind); } protected boolean isInterestingKind(GrailsElementKind kind) { return DOMAIN_CLASS==kind || CONTROLLER_CLASS == kind || SERVICE_CLASS == kind || TAGLIB_CLASS == kind || UNIT_TEST == kind || INTEGRATION_TEST == kind; } @Override public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException { RefactoringStatus status = new RefactoringStatus(); try { extraRenamings = computeExtraRenamings(status, pm); if (!status.hasFatalError()) { chooseAdditionalRenamings(status); checkAdditionalRenamings(status, pm, context); computeChanges(status, pm); computeExtraChanges(status, pm); } } catch (CoreException e) { GrailsCoreActivator.log(e); status.addError("Internal error see error log for details"); } return status; } /** * Searches for extra references to renamed Grails types and does grails specific things to update those references. * For example, when a service is renamed we should look for auto-wired fields that refer to this service by naming * convention and also rename those fields and references to them. */ private void computeExtraChanges(RefactoringStatus status, IProgressMonitor pm) { computeExtraChanges(this, status, pm); for (ITypeRenaming r : extraRenamings) { computeExtraChanges(r, status, pm); } } private void computeExtraChanges(ITypeRenaming r, RefactoringStatus status, IProgressMonitor pm) { for (ExtraChangeComputer computer : getExtraChangeComputers()) { if (computer.initialize(project, r)) { computer.createChanges(changes, status, pm); } } } private Collection<ExtraChangeComputer> getExtraChangeComputers() { IGrailsTypeRenameConfigurer configurer = getConfigurer(); ArrayList<ExtraChangeComputer> result = new ArrayList<ExtraChangeComputer>(); if (configurer.updateServiceReferences()) { result.add(new ServiceReferenceUpdater()); } if (configurer.updateGSPs()) { result.add(new GSPUpdater()); } result.add(new ControllerReferenceUpdater()); return result; } protected Collection<ITypeRenaming> computeExtraRenamings(RefactoringStatus status, IProgressMonitor pm) { GrailsTypeRenameRefactoring refactoring = getGrailsTypeRefactoring(); ExtraRenamingsComputer extraRenamingsComputer; if (refactoring!=null) { extraRenamingsComputer = refactoring.getExtraRenamingsComputer(); grailsRenameConfigurer = refactoring.getRenameConfigurer(); } else { extraRenamingsComputer = ExtraRenamingsComputer.create(this); grailsRenameConfigurer = new DialogBasedGrailsTypeRenameConfigurer(); } if (extraRenamingsComputer != null) { status.merge(extraRenamingsComputer.checkPreconditions()); return extraRenamingsComputer.getExtraRenamings(pm); } return new ArrayList<ITypeRenaming>(); } /** * @return The GrailsTypeRenameRefactoring we are participating with, or null, if the refactoring we are * partici[ating with is a different type of refactoring. */ private GrailsTypeRenameRefactoring getGrailsTypeRefactoring() { ProcessorBasedRefactoring refactoring = getProcessor().getRefactoring(); if (refactoring instanceof GrailsTypeRenameRefactoring) { return (GrailsTypeRenameRefactoring) refactoring; } return null; } private void checkAdditionalRenamings(RefactoringStatus status, IProgressMonitor pm, CheckConditionsContext context) throws CoreException { extraRenamers = new ArrayList<ExtraTypeRenamer>(); for (ITypeRenaming renaming : extraRenamings) { ExtraTypeRenamer renamer = new ExtraTypeRenamer(renaming); extraRenamers.add(renamer); renamer.checkConditions(status, pm, context); } } private void chooseAdditionalRenamings(RefactoringStatus status) { if (!extraRenamings.isEmpty()) { Collection<ITypeRenaming> chosenRenamings = grailsRenameConfigurer.chooseAdditionalRenamings( new TypeRenaming(type, getArguments().getNewName()), extraRenamings, status); extraRenamings = new ArrayList<ITypeRenaming>(); for (ITypeRenaming r : chosenRenamings) { extraRenamings.add(r); } } } private void computeChanges(RefactoringStatus status, IProgressMonitor pm) { try { changes = new ParticipantChangeManager(this); if (!extraRenamers.isEmpty()) { for (ExtraTypeRenamer ren : extraRenamers) { Change change = ren.createChange(pm); changes.add(change); } } } catch (Exception e) { GrailsCoreActivator.log(e); } } @Override public Change createPreChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { if (changes!=null) { changes.copyExistingChangesTo(this); return changes.getNewTextChanges(); } return null; } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { if (changes!=null) { return changes.getOtherChanges(); } return null; } public IType getTarget() { return type; } public String getNewName() { return getArguments().getNewName(); } @Override public String getName() { return "Grails Type Rename Participant"; } }