| /******************************************************************************* |
| * Copyright (c) 2004, 2008 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.wst.jsdt.web.ui.internal.java.refactoring; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| 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.RefactoringStatus; |
| 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.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.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| import org.eclipse.wst.jsdt.core.IJavaScriptElement; |
| import org.eclipse.wst.jsdt.core.search.SearchDocument; |
| import org.eclipse.wst.jsdt.core.search.SearchMatch; |
| import org.eclipse.wst.jsdt.core.search.SearchRequestor; |
| import org.eclipse.wst.jsdt.web.core.javascript.search.JSDTSearchDocumentDelegate; |
| import org.eclipse.wst.jsdt.web.core.javascript.search.JsSearchSupport; |
| import org.eclipse.wst.jsdt.web.ui.internal.JsUIMessages; |
| import org.eclipse.wst.jsdt.web.ui.internal.Logger; |
| import org.eclipse.wst.sse.core.internal.document.DocumentReader; |
| import org.eclipse.wst.sse.core.internal.encoding.CodedStreamCreator; |
| |
| /** |
| * |
| |
| * Provisional API: This class/interface is part of an interim API that is still under development and expected to |
| * change significantly before reaching stability. It is being made available at this early stage to solicit feedback |
| * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken |
| * (repeatedly) as the API evolves. |
| * |
| * @author pavery |
| */ |
| public class BasicRefactorSearchRequestor extends SearchRequestor { |
| /** |
| * Change class that wraps a text edit on the jsp document |
| */ |
| private class RenameChange extends DocumentChange { |
| private String fDescription = JsUIMessages.BasicRefactorSearchRequestor_0; |
| private TextEdit fEdit = null; |
| private IDocument fJSPDoc = null; |
| private IFile fJSPFile = null; |
| |
| public RenameChange(IFile jspFile, IDocument jspDoc, TextEdit edit, String description) { |
| super(JsUIMessages.BasicRefactorSearchRequestor_6, jspDoc); |
| this.fEdit = edit; |
| this.fJSPFile = jspFile; |
| this.fJSPDoc = jspDoc; |
| this.fDescription = description; |
| } |
| |
| |
| public Object getModifiedElement() { |
| return getElement(); |
| } |
| |
| |
| public String getName() { |
| return this.fDescription; |
| } |
| |
| |
| public IDocument getPreviewDocument(IProgressMonitor pm) throws CoreException { |
| IDocument copyDoc = new Document(fJSPDoc.get()); |
| try { |
| fEdit.apply(copyDoc); |
| } catch (MalformedTreeException e) { |
| // ignore |
| } catch (BadLocationException e) { |
| // ignore |
| } |
| return copyDoc; |
| } |
| |
| /** |
| * Checks if a document is open in an editor |
| * |
| * @param jspDoc |
| * @return |
| */ |
| private boolean isOpenInEditor(IDocument jspDoc) { |
| IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows(); |
| IWorkbenchWindow w = null; |
| for (int i = 0; i < windows.length; i++) { |
| w = windows[i]; |
| IWorkbenchPage page = w.getActivePage(); |
| if (page != null) { |
| IEditorReference[] references = page.getEditorReferences(); |
| IEditorPart editor = null; |
| Object o = null; |
| IDocument doc = null; |
| for (int j = 0; j < references.length; j++) { |
| editor = references[j].getEditor(true); |
| // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3764 |
| // use adapter to get ITextEditor (for things like |
| // page designer) |
| o = editor.getAdapter(ITextEditor.class); |
| if (o != null && o instanceof ITextEditor) { |
| doc = ((ITextEditor) o).getDocumentProvider().getDocument(editor.getEditorInput()); |
| if (doc != null && doc.equals(jspDoc)) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException { |
| return new RefactoringStatus(); |
| } |
| |
| |
| public Change perform(IProgressMonitor pm) throws CoreException { |
| RenameChange undoChange = null; |
| try { |
| if (!isOpenInEditor(this.fJSPDoc)) { |
| // apply edit to JSP doc AND save model |
| undoChange = new RenameChange(this.fJSPFile, this.fJSPDoc, this.fEdit.apply(fJSPDoc), this.fDescription); |
| saveFile(this.fJSPFile, this.fJSPDoc); |
| } else { |
| // just apply edit to JSP document |
| undoChange = new RenameChange(this.fJSPFile, this.fJSPDoc, this.fEdit.apply(fJSPDoc), this.fDescription); |
| } |
| } catch (MalformedTreeException e) { |
| Logger.logException(e); |
| } catch (BadLocationException e) { |
| Logger.logException(e); |
| } |
| return undoChange; |
| } |
| |
| /** |
| * Performed in an operation since it modifies resources in the |
| * workspace |
| * |
| * @param jspDoc |
| * @throws CoreException |
| */ |
| private void saveFile(IFile jspFile, IDocument jspDoc) { |
| SaveJspFileOp op = new SaveJspFileOp(jspFile, jspDoc); |
| try { |
| op.run(JsSearchSupport.getInstance().getProgressMonitor()); |
| } catch (InvocationTargetException e) { |
| Logger.logException(e); |
| } catch (InterruptedException e) { |
| Logger.logException(e); |
| } |
| } |
| } |
| // end inner class SaveJspFileOp |
| /** |
| * Workspace operation to perform save on model for updated documents. |
| * Should only be done on models not open in an editor. |
| */ |
| private class SaveJspFileOp extends WorkspaceModifyOperation { |
| private IDocument fJSPDoc = null; |
| private IFile fJSPFile = null; |
| |
| public SaveJspFileOp(IFile jspFile, IDocument jspDoc) { |
| this.fJSPDoc = jspDoc; |
| this.fJSPFile = jspFile; |
| } |
| |
| |
| protected void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { |
| // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3765 |
| // save file w/ no intermediate model creation |
| CodedStreamCreator codedStreamCreator = new CodedStreamCreator(); |
| Reader reader = new DocumentReader(this.fJSPDoc); |
| codedStreamCreator.set(this.fJSPFile, reader); |
| ByteArrayOutputStream codedByteStream = null; |
| InputStream codedStream = null; |
| try { |
| codedByteStream = codedStreamCreator.getCodedByteArrayOutputStream(); |
| codedStream = new ByteArrayInputStream(codedByteStream.toByteArray()); |
| if (this.fJSPFile.exists()) { |
| this.fJSPFile.setContents(codedStream, true, true, null); |
| } else { |
| this.fJSPFile.create(codedStream, false, null); |
| } |
| } catch (CoreException e) { |
| Logger.logException(e); |
| } catch (IOException e) { |
| Logger.logException(e); |
| } finally { |
| try { |
| if (codedByteStream != null) { |
| codedByteStream.close(); |
| } |
| if (codedStream != null) { |
| codedStream.close(); |
| } |
| } catch (IOException e) { |
| // unlikely |
| } |
| } |
| } |
| } |
| // end inner class RenameChange |
| /** The type being renamed (the old type) */ |
| IJavaScriptElement 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(IJavaScriptElement element, String newName) { |
| this.fNewName = newName; |
| this.fElement = element; |
| this.fSearchDocPath2JavaEditMap = new HashMap(); |
| } |
| |
| /** |
| * @see org.eclipse.wst.jsdt.core.search.SearchRequestor#acceptSearchMatch(org.eclipse.wst.jsdt.core.search.SearchMatch) |
| */ |
| |
| public void acceptSearchMatch(SearchMatch javaMatch) throws CoreException { |
| String matchDocumentPath = javaMatch.getResource().getFullPath().toString(); |
| SearchDocument searchDoc = JsSearchSupport.getInstance().getSearchDocument(matchDocumentPath); |
| if (searchDoc != null && searchDoc instanceof JSDTSearchDocumentDelegate) { |
| String renameText = getRenameText((JSDTSearchDocumentDelegate) searchDoc, javaMatch); |
| // add it for the correct document |
| addJavaEdit(searchDoc.getPath(), new ReplaceEdit(javaMatch.getOffset(), javaMatch.getLength(), renameText)); |
| } |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| |
| private Change createChange(JSDTSearchDocumentDelegate searchDoc, TextEdit edit) { |
| IDocument doc = searchDoc.getJspTranslation().getHtmlDocument(); |
| 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(JsUIMessages.BasicRefactorSearchRequestor_1, new String[] { file, lineNumber }); //$NON-NLS-1$ |
| } catch (BadLocationException e) { |
| Logger.logException(e); |
| } |
| return new RenameChange(searchDoc.getFile(), doc, edit, description); |
| } |
| |
| /** |
| * |
| * @return all JSP changes for the search matches for the given Type |
| */ |
| public Change[] getChanges() { |
| JsSearchSupport support = JsSearchSupport.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 JSDTSearchDocumentDelegate) { |
| JSDTSearchDocumentDelegate javaDelegate = (JSDTSearchDocumentDelegate) delegate; |
| changes.add(createChange(javaDelegate, javaEdit)); |
| } |
| } |
| return (Change[]) changes.toArray(new Change[changes.size()]); |
| } |
| |
| /** |
| * Subclasses should override to better describe the change. |
| * |
| * @return |
| */ |
| protected String getDescription() { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| public IJavaScriptElement getElement() { |
| return this.fElement; |
| } |
| |
| /** |
| * @return the new name for the Type |
| */ |
| public String getNewName() { |
| return this.fNewName; |
| } |
| |
| /** |
| * @param searchDoc |
| * @return |
| */ |
| protected String getRenameText(JSDTSearchDocumentDelegate searchDoc, SearchMatch javaMatch) { |
| return getNewName(); |
| } |
| } |