blob: a057ed573dedb0ee7b08f0067a1581ed19a0af09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2010 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.jsp.ui.internal.java.refactoring;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.search.SearchDocument;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jst.jsp.core.internal.java.search.JSPSearchSupport;
import org.eclipse.jst.jsp.core.internal.java.search.JavaSearchDocumentDelegate;
import org.eclipse.jst.jsp.ui.internal.JSPUIMessages;
import org.eclipse.jst.jsp.ui.internal.Logger;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.osgi.util.NLS;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;
/**
* <p>After a search is run with this {@link SearchRequestor} {@link #getChanges(RefactoringParticipant)}
* can be called to get any new {@link Change}s that need to be created as a result of the search. If
* {@link Change}s are already existing for the documents found then new {@link Change}s will not be
* created for them, but the needed {@link TextEdit}s will be added to the existing {@link Change}s.</p>
*/
public class BasicRefactorSearchRequestor extends SearchRequestor {
/** The type being renamed (the old type)*/
IJavaElement fElement = null;
/** The new name of the type being renamed*/
private String fNewName = ""; //$NON-NLS-1$
/** maps a JSPSearchDocument path -> MultiTextEdit for the java file*/
private HashMap fSearchDocPath2JavaEditMap = null;
public BasicRefactorSearchRequestor(IJavaElement element, String newName) {
this.fNewName = newName;
this.fElement = element;
this.fSearchDocPath2JavaEditMap = new HashMap();
}
public IJavaElement getElement() {
return this.fElement;
}
/**
* @return the new name for the Type
*/
public String getNewName() {
return this.fNewName;
}
/**
* @see org.eclipse.jdt.core.search.SearchRequestor#acceptSearchMatch(org.eclipse.jdt.core.search.SearchMatch)
*/
public void acceptSearchMatch(SearchMatch javaMatch) throws CoreException {
String matchDocumentPath = javaMatch.getResource().getFullPath().toString();
SearchDocument searchDoc = JSPSearchSupport.getInstance().getSearchDocument(matchDocumentPath);
if (searchDoc != null && searchDoc instanceof JavaSearchDocumentDelegate) {
String renameText = getRenameText((JavaSearchDocumentDelegate)searchDoc, javaMatch);
//if rename text is null then don't create an edit for it
if(renameText != null) {
// add it for the correct document
addJavaEdit(searchDoc.getPath(), new ReplaceEdit(javaMatch.getOffset(), javaMatch.getLength(), renameText));
}
}
}
/**
* @param searchDoc
* @return the rename text or <code>null</code> if no edit should be created for the given match.
* Such a case would be a match where nothing needs to be edited.
*/
protected String getRenameText(JavaSearchDocumentDelegate searchDoc, SearchMatch javaMatch) {
return getNewName();
}
/**
* Adds to the multi edit for a give java document.
* @param javaDocument
* @param javaEdit
*/
private void addJavaEdit(String searchDocPath, ReplaceEdit javaEdit) {
Object o = this.fSearchDocPath2JavaEditMap.get(searchDocPath);
if(o != null) {
MultiTextEdit multi = (MultiTextEdit)o;
multi.addChild(javaEdit);
}
else {
// use a multi edit so doc position offsets get updated automatically
// when adding multiple child edits
MultiTextEdit multi = new MultiTextEdit();
multi.addChild(javaEdit);
this.fSearchDocPath2JavaEditMap.put(searchDocPath, multi);
}
}
/**
* <p>This function is not safe because it does not check for existing {@link Change}s that
* new {@link Change}s created by this method may conflict with. These conflicts can
* cause indeterminate results when applied to documents. Long story short, don't
* use this method any more.</p>
*
* @return all JSP changes for the search matches for the given Type, they may conflict
* with already existing {@link Change}s
*
* @see #getChanges(RefactoringParticipant)
*
* @deprecated
*/
public Change[] getChanges() {
JSPSearchSupport support = JSPSearchSupport.getInstance();
List changes = new ArrayList();
Iterator keys = fSearchDocPath2JavaEditMap.keySet().iterator();
String searchDocPath = null;
SearchDocument delegate = null;
while(keys.hasNext()) {
// create on the fly
searchDocPath = (String)keys.next();
MultiTextEdit javaEdit = (MultiTextEdit)fSearchDocPath2JavaEditMap.get(searchDocPath);
delegate = support.getSearchDocument(searchDocPath);
if(delegate != null && delegate instanceof JavaSearchDocumentDelegate) {
JavaSearchDocumentDelegate javaDelegate = (JavaSearchDocumentDelegate)delegate;
changes.add(createChange(javaDelegate, javaDelegate.getJspTranslation().getJspEdit(javaEdit)));
}
}
return (Change[])changes.toArray(new Change[changes.size()]);
}
/**
* Gets new {@link Change}s created as a result of this {@link SearchRequestor}.
* Any existing {@link TextChange}s that had new edits added to them will not be
* returned.
*
* @param participant {@link RefactoringParticipant} to determine if there are already existing
* {@link TextChange}s for the documents that this {@link SearchRequestor} found.
* If existing
* {@link TextChange}s are found then they will be used for any new edits, else new {@link TextChange}s
* will be created.
*
* @return Any new {@link TextChange}s created by this {@link SearchRequestor}. If edits were
* added to existing {@link TextChange}s then those existing {@link TextChange}s will not be
* returned in this array.
*/
public Change[] getChanges(RefactoringParticipant participant) {
JSPSearchSupport support = JSPSearchSupport.getInstance();
List changes = new ArrayList();
Iterator keys = fSearchDocPath2JavaEditMap.keySet().iterator();
String searchDocPath = null;
SearchDocument delegate = null;
while(keys.hasNext()) {
// create on the fly
searchDocPath = (String)keys.next();
MultiTextEdit javaEdit = (MultiTextEdit)fSearchDocPath2JavaEditMap.get(searchDocPath);
delegate = support.getSearchDocument(searchDocPath);
if(delegate != null && delegate instanceof JavaSearchDocumentDelegate) {
JavaSearchDocumentDelegate javaDelegate = (JavaSearchDocumentDelegate)delegate;
Change change = createChange(javaDelegate, javaDelegate.getJspTranslation().getJspEdit(javaEdit), participant);
changes.add(change);
}
}
return (Change[])changes.toArray(new Change[changes.size()]);
}
/**
* <p>This method is not safe because it does not take into consideration already existing
* {@link Change}s and thus conflicts could occur that when applied create indeterminate
* results in the target documents</p>
*
* @see #createChange(JavaSearchDocumentDelegate, TextEdit, RefactoringParticipant)
*
* @deprecated
*/
private Change createChange(JavaSearchDocumentDelegate searchDoc, TextEdit edit) {
IDocument doc = searchDoc.getJspTranslation().getJspDocument();
String file = searchDoc.getFile().getName();
String description = getDescription();
try {
// document lines are 0 based
String lineNumber = Integer.toString(doc.getLineOfOffset(edit.getOffset()) + 1);
description += " " + NLS.bind(JSPUIMessages.BasicRefactorSearchRequestor_1, new String[]{file, lineNumber}); //$NON-NLS-1$
}
catch (BadLocationException e) {
Logger.logException(e);
}
return new JSPRenameChange(searchDoc.getFile(), doc, edit, description);
}
/**
* </p>If a {@link TextChange} does not already exist for the given {@link JavaSearchDocumentDelegate}
* then a new one will be created with the given {@link TextEdit}. Otherwise the given {@link TextEdit}
* will be added to a new group and added to the existing change and <code>null</code> will be returned.</p>
*
* @param searchDoc the {@link JavaSearchDocumentDelegate} that the <code>edit</code> will be applied to
* @param edit the {@link TextEdit} that needs to be added to a new {@link TextChange} or appended to an
* existing one
* @param participant the {@link RefactoringParticipant} that knows about the existing {@link TextChange}s
* @return a new {@link Change} if there was not one already existing for the document in question,
* else <code>null</code>
*/
private Change createChange(JavaSearchDocumentDelegate searchDoc, TextEdit edit, RefactoringParticipant participant) {
IDocument doc = searchDoc.getJspTranslation().getJspDocument();
String description = getDescription();
TextChange existingChange = participant.getTextChange(searchDoc.getFile());
TextChange change = null;
if(existingChange != null) {
try {
existingChange.addEdit(edit);
}catch (MalformedTreeException e) {
Logger.logException("MalformedTreeException while adding edit " + //$NON-NLS-1$
edit + " to existing change " + change, e); //$NON-NLS-1$
}
TextEditGroup group = new TextEditGroup(description, edit);
existingChange.addTextEditGroup(group);
} else {
change = new JSPRenameChange(searchDoc.getFile(), doc, edit, searchDoc.getFile().getName());
TextEditGroup group = new TextEditGroup(description, edit);
change.addTextEditGroup(group);
}
return change;
}
// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3205
// only relevant for IType refactorings
protected boolean isFullyQualified(String matchText) {
if(getElement() instanceof IType) {
String pkg = ((IType)getElement()).getPackageFragment().getElementName();
return matchText.startsWith(pkg);
}
return false;
}
/**
* Subclasses should override to better describe the change.
* @return
*/
protected String getDescription() {
return ""; //$NON-NLS-1$
}
}