development
Revisão | b6007ebd5c6f373ab4bc48363b6738b681ef13a3 (tree) |
---|---|
Hora | 2009-03-25 12:57:57 |
Autor | Raphael Moll <> |
Commiter | The Android Open Source Project |
Automated import from //branches/donutburger/...@141823,141823
@@ -16,7 +16,11 @@ | ||
16 | 16 | |
17 | 17 | package com.android.ide.eclipse.adt.refactorings.extractstring; |
18 | 18 | |
19 | +import com.android.ide.eclipse.common.AndroidConstants; | |
20 | + | |
19 | 21 | import org.eclipse.core.resources.IFile; |
22 | +import org.eclipse.core.resources.IProject; | |
23 | +import org.eclipse.core.runtime.CoreException; | |
20 | 24 | import org.eclipse.jdt.core.ICompilationUnit; |
21 | 25 | import org.eclipse.jdt.core.ITypeRoot; |
22 | 26 | import org.eclipse.jdt.core.JavaCore; |
@@ -32,6 +36,7 @@ import org.eclipse.ui.IWorkbenchPage; | ||
32 | 36 | import org.eclipse.ui.IWorkbenchWindow; |
33 | 37 | import org.eclipse.ui.IWorkbenchWindowActionDelegate; |
34 | 38 | import org.eclipse.ui.PlatformUI; |
39 | +import org.eclipse.ui.part.FileEditorInput; | |
35 | 40 | |
36 | 41 | /* |
37 | 42 | * Quick Reference Link: |
@@ -71,7 +76,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { | ||
71 | 76 | /** Keep track of the current workbench window. */ |
72 | 77 | private IWorkbenchWindow mWindow; |
73 | 78 | private ITextSelection mSelection; |
74 | - private ICompilationUnit mUnit; | |
79 | + private IFile mFile; | |
75 | 80 | |
76 | 81 | /** |
77 | 82 | * Keep track of the current workbench window. |
@@ -99,30 +104,24 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { | ||
99 | 104 | // runs since we don't have access to the AST yet. |
100 | 105 | |
101 | 106 | mSelection = null; |
102 | - mUnit = null; | |
107 | + mFile = null; | |
103 | 108 | |
104 | 109 | if (selection instanceof ITextSelection) { |
105 | 110 | mSelection = (ITextSelection) selection; |
106 | 111 | if (mSelection.getLength() > 0) { |
107 | - mUnit = getCompilationUnit(); | |
112 | + mFile = getSelectedFile(); | |
108 | 113 | } |
109 | - | |
110 | - // Keep for debugging purposes | |
111 | - //System.out.println(String.format("-- Selection: %d + %d = %s", | |
112 | - // mSelection.getOffset(), | |
113 | - // mSelection.getLength(), | |
114 | - // mSelection.getText())); | |
115 | 114 | } |
116 | 115 | |
117 | - action.setEnabled(mSelection != null && mUnit != null); | |
116 | + action.setEnabled(mSelection != null && mFile != null); | |
118 | 117 | } |
119 | 118 | |
120 | 119 | /** |
121 | 120 | * Create a new instance of our refactoring and a wizard to configure it. |
122 | 121 | */ |
123 | 122 | public void run(IAction action) { |
124 | - if (mSelection != null && mUnit != null) { | |
125 | - ExtractStringRefactoring ref = new ExtractStringRefactoring(mUnit, mSelection); | |
123 | + if (mSelection != null && mFile != null) { | |
124 | + ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection); | |
126 | 125 | RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String"); |
127 | 126 | RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); |
128 | 127 | try { |
@@ -134,9 +133,14 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { | ||
134 | 133 | } |
135 | 134 | |
136 | 135 | /** |
137 | - * Returns the active {@link ICompilationUnit} or null. | |
136 | + * Returns the active {@link IFile} (hopefully matching our selection) or null. | |
137 | + * The file is only returned if it's a file from a project with an Android nature. | |
138 | + * <p/> | |
139 | + * At that point we do not try to analyze if the selection nor the file is suitable | |
140 | + * for the refactoring. This check is performed when the refactoring is invoked since | |
141 | + * it can then produce meaningful error messages as needed. | |
138 | 142 | */ |
139 | - private ICompilationUnit getCompilationUnit() { | |
143 | + private IFile getSelectedFile() { | |
140 | 144 | IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
141 | 145 | if (wwin != null) { |
142 | 146 | IWorkbenchPage page = wwin.getActivePage(); |
@@ -144,12 +148,19 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { | ||
144 | 148 | IEditorPart editor = page.getActiveEditor(); |
145 | 149 | if (editor != null) { |
146 | 150 | IEditorInput input = editor.getEditorInput(); |
147 | - if (input != null) { | |
148 | - ITypeRoot typeRoot = JavaUI.getEditorInputTypeRoot(input); | |
149 | - // The type root can be either a .class or a .java (aka compilation unit). | |
150 | - // We want the compilation unit kind. | |
151 | - if (typeRoot instanceof ICompilationUnit) { | |
152 | - return (ICompilationUnit) typeRoot; | |
151 | + | |
152 | + if (input instanceof FileEditorInput) { | |
153 | + FileEditorInput fi = (FileEditorInput) input; | |
154 | + IFile file = fi.getFile(); | |
155 | + if (file.exists()) { | |
156 | + IProject proj = file.getProject(); | |
157 | + try { | |
158 | + if (proj != null && proj.hasNature(AndroidConstants.NATURE)) { | |
159 | + return file; | |
160 | + } | |
161 | + } catch (CoreException e) { | |
162 | + // ignore | |
163 | + } | |
153 | 164 | } |
154 | 165 | } |
155 | 166 | } |
@@ -24,7 +24,9 @@ import org.eclipse.core.resources.IFile; | ||
24 | 24 | import org.eclipse.core.resources.IProject; |
25 | 25 | import org.eclipse.core.resources.IResource; |
26 | 26 | import org.eclipse.core.resources.ResourceAttributes; |
27 | +import org.eclipse.core.resources.ResourcesPlugin; | |
27 | 28 | import org.eclipse.core.runtime.CoreException; |
29 | +import org.eclipse.core.runtime.IPath; | |
28 | 30 | import org.eclipse.core.runtime.IProgressMonitor; |
29 | 31 | import org.eclipse.core.runtime.OperationCanceledException; |
30 | 32 | import org.eclipse.core.runtime.Path; |
@@ -126,46 +128,54 @@ import javax.xml.xpath.XPathExpressionException; | ||
126 | 128 | */ |
127 | 129 | class ExtractStringRefactoring extends Refactoring { |
128 | 130 | |
129 | - /** The compilation unit, a.k.a. the Java file model. */ | |
130 | - private final ICompilationUnit mUnit; | |
131 | + /** The file model being manipulated. */ | |
132 | + private final IFile mFile; | |
133 | + /** The start of the selection in {@link #mFile}. */ | |
131 | 134 | private final int mSelectionStart; |
135 | + /** The end of the selection in {@link #mFile}. */ | |
132 | 136 | private final int mSelectionEnd; |
137 | + | |
138 | + /** The compilation unit, only defined if {@link #mFile} points to a usable Java source file. */ | |
139 | + private ICompilationUnit mUnit; | |
133 | 140 | /** The actual string selected, after UTF characters have been escaped, good for display. */ |
134 | 141 | private String mTokenString; |
135 | - /** Start position of the string token in the source buffer. */ | |
136 | - private int mTokenStart; | |
137 | - /** End position of the string token in the source buffer. */ | |
138 | - private int mTokenEnd; | |
142 | + | |
143 | + /** The XML string ID selected by the user in the wizard. */ | |
139 | 144 | private String mXmlStringId; |
145 | + /** The path of the XML file that will define {@link #mXmlStringId}, selected by the user | |
146 | + * in the wizard. */ | |
140 | 147 | private String mTargetXmlFileWsPath; |
148 | + | |
149 | + /** A temporary cache of R.string IDs defined by a given xml file. The key is the | |
150 | + * project path of the file, the data is a set of known string Ids for that file. */ | |
141 | 151 | private HashMap<String,HashSet<String>> mResIdCache; |
152 | + /** An instance of XPath, created lazily on demand. */ | |
142 | 153 | private XPath mXPath; |
154 | + /** The list of changes computed by {@link #checkFinalConditions(IProgressMonitor)} and | |
155 | + * used by {@link #createChange(IProgressMonitor)}. */ | |
143 | 156 | private ArrayList<Change> mChanges; |
144 | 157 | |
145 | 158 | public ExtractStringRefactoring(Map<String, String> arguments) |
146 | 159 | throws NullPointerException { |
147 | - mUnit = (ICompilationUnit) JavaCore.create(arguments.get("CU")); //$NON-NLS-1$ | |
160 | + | |
161 | + IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$ | |
162 | + mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path); | |
148 | 163 | mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$ |
149 | 164 | mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$ |
150 | - mTokenStart = Integer.parseInt(arguments.get("tok-start")); //$NON-NLS-1$ | |
151 | - mTokenEnd = Integer.parseInt(arguments.get("tok-end")); //$NON-NLS-1$ | |
152 | 165 | mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$ |
153 | 166 | } |
154 | 167 | |
155 | 168 | private Map<String, String> createArgumentMap() { |
156 | 169 | HashMap<String, String> args = new HashMap<String, String>(); |
157 | - args.put("CU", mUnit.getHandleIdentifier()); //$NON-NLS-1$ | |
170 | + args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$ | |
158 | 171 | args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$ |
159 | 172 | args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$ |
160 | - args.put("tok-start", Integer.toString(mTokenStart)); //$NON-NLS-1$ | |
161 | - args.put("tok-end", Integer.toString(mTokenEnd)); //$NON-NLS-1$ | |
162 | 173 | args.put("tok-esc", mTokenString); //$NON-NLS-1$ |
163 | 174 | return args; |
164 | 175 | } |
165 | 176 | |
166 | - public ExtractStringRefactoring(ICompilationUnit unit, ITextSelection selection) { | |
167 | - mUnit = unit; | |
168 | - | |
177 | + public ExtractStringRefactoring(IFile file, ITextSelection selection) { | |
178 | + mFile = file; | |
169 | 179 | mSelectionStart = selection.getOffset(); |
170 | 180 | mSelectionEnd = mSelectionStart + selection.getLength(); |
171 | 181 | } |
@@ -207,75 +217,42 @@ class ExtractStringRefactoring extends Refactoring { | ||
207 | 217 | public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) |
208 | 218 | throws CoreException, OperationCanceledException { |
209 | 219 | |
220 | + mUnit = null; | |
210 | 221 | mTokenString = null; |
211 | - mTokenStart = -1; | |
212 | - mTokenEnd = -1; | |
213 | 222 | |
214 | 223 | RefactoringStatus status = new RefactoringStatus(); |
215 | 224 | |
216 | 225 | try { |
217 | - monitor.beginTask("Checking preconditions...", 3); | |
218 | - | |
219 | - if (!extraChecks(monitor, status)) { | |
226 | + monitor.beginTask("Checking preconditions...", 5); | |
227 | + | |
228 | + if (!checkSourceFile(mFile, status, monitor)) { | |
220 | 229 | return status; |
221 | 230 | } |
222 | - | |
231 | + | |
232 | + // Try to get a compilation unit from this file. If it fails, mUnit is null. | |
223 | 233 | try { |
224 | - IBuffer buffer = mUnit.getBuffer(); | |
225 | - | |
226 | - IScanner scanner = ToolFactory.createScanner( | |
227 | - false, //tokenizeComments | |
228 | - false, //tokenizeWhiteSpace | |
229 | - false, //assertMode | |
230 | - false //recordLineSeparator | |
231 | - ); | |
232 | - scanner.setSource(buffer.getCharacters()); | |
233 | - monitor.worked(1); | |
234 | - | |
235 | - for(int token = scanner.getNextToken(); | |
236 | - token != ITerminalSymbols.TokenNameEOF; | |
237 | - token = scanner.getNextToken()) { | |
238 | - if (scanner.getCurrentTokenStartPosition() <= mSelectionStart && | |
239 | - scanner.getCurrentTokenEndPosition() >= mSelectionEnd) { | |
240 | - // found the token, but only keep of the right type | |
241 | - if (token == ITerminalSymbols.TokenNameStringLiteral) { | |
242 | - mTokenString = new String(scanner.getCurrentTokenSource()); | |
243 | - mTokenStart = scanner.getCurrentTokenStartPosition(); | |
244 | - mTokenEnd = scanner.getCurrentTokenEndPosition(); | |
245 | - } | |
246 | - break; | |
247 | - } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) { | |
248 | - // scanner is past the selection, abort. | |
249 | - break; | |
250 | - } | |
251 | - } | |
252 | - } catch (JavaModelException e1) { | |
253 | - // Error in mUnit.getBuffer. Ignore. | |
254 | - } catch (InvalidInputException e2) { | |
255 | - // Error in scanner.getNextToken. Ignore. | |
256 | - } finally { | |
257 | - monitor.worked(1); | |
258 | - } | |
234 | + mUnit = JavaCore.createCompilationUnitFrom(mFile); | |
259 | 235 | |
260 | - if (mTokenString != null) { | |
261 | - // As a literal string, the token should have surrounding quotes. Remove them. | |
262 | - int len = mTokenString.length(); | |
263 | - if (len > 0 && | |
264 | - mTokenString.charAt(0) == '"' && | |
265 | - mTokenString.charAt(len - 1) == '"') { | |
266 | - mTokenString = mTokenString.substring(1, len - 1); | |
236 | + // Make sure the unit is not read-only, e.g. it's not a class file or inside a Jar | |
237 | + if (mUnit.isReadOnly()) { | |
238 | + status.addFatalError("The file is read-only, please make it writeable first."); | |
239 | + return status; | |
267 | 240 | } |
268 | - // We need a non-empty string literal | |
269 | - if (mTokenString.length() == 0) { | |
270 | - mTokenString = null; | |
241 | + | |
242 | + // This is a Java file. Check if it contains the selection we want. | |
243 | + if (!findSelectionInJavaUnit(mUnit, status, monitor)) { | |
244 | + return status; | |
271 | 245 | } |
246 | + | |
247 | + } catch (Exception e) { | |
248 | + // That was not a Java file. Ignore. | |
272 | 249 | } |
273 | 250 | |
274 | - if (mTokenString == null) { | |
275 | - status.addFatalError("Please select a Java string literal."); | |
251 | + if (mUnit == null) { | |
252 | + // Check this an XML file and get the selection and its context. | |
253 | + // TODO | |
254 | + status.addFatalError("Selection must be inside a Java source file."); | |
276 | 255 | } |
277 | - | |
278 | - monitor.worked(1); | |
279 | 256 | } finally { |
280 | 257 | monitor.done(); |
281 | 258 | } |
@@ -284,30 +261,93 @@ class ExtractStringRefactoring extends Refactoring { | ||
284 | 261 | } |
285 | 262 | |
286 | 263 | /** |
287 | - * Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit() | |
288 | - * Might not be useful. | |
264 | + * Try to find the selected Java element in the compilation unit. | |
289 | 265 | * |
290 | - * @return False if caller should abort, true if caller should continue. | |
266 | + * If selection matches a string literal, capture it, otherwise add a fatal error | |
267 | + * to the status. | |
268 | + * | |
269 | + * On success, advance the monitor by 3. | |
291 | 270 | */ |
292 | - private boolean extraChecks(IProgressMonitor monitor, RefactoringStatus status) { | |
293 | - // | |
294 | - IResource res = mUnit.getPrimary().getResource(); | |
295 | - if (res == null || res.getType() != IResource.FILE) { | |
296 | - status.addFatalError("Cannot access resource; only regular files can be used."); | |
297 | - return false; | |
271 | + private boolean findSelectionInJavaUnit(ICompilationUnit unit, | |
272 | + RefactoringStatus status, IProgressMonitor monitor) { | |
273 | + try { | |
274 | + IBuffer buffer = unit.getBuffer(); | |
275 | + | |
276 | + IScanner scanner = ToolFactory.createScanner( | |
277 | + false, //tokenizeComments | |
278 | + false, //tokenizeWhiteSpace | |
279 | + false, //assertMode | |
280 | + false //recordLineSeparator | |
281 | + ); | |
282 | + scanner.setSource(buffer.getCharacters()); | |
283 | + monitor.worked(1); | |
284 | + | |
285 | + for(int token = scanner.getNextToken(); | |
286 | + token != ITerminalSymbols.TokenNameEOF; | |
287 | + token = scanner.getNextToken()) { | |
288 | + if (scanner.getCurrentTokenStartPosition() <= mSelectionStart && | |
289 | + scanner.getCurrentTokenEndPosition() >= mSelectionEnd) { | |
290 | + // found the token, but only keep of the right type | |
291 | + if (token == ITerminalSymbols.TokenNameStringLiteral) { | |
292 | + mTokenString = new String(scanner.getCurrentTokenSource()); | |
293 | + } | |
294 | + break; | |
295 | + } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) { | |
296 | + // scanner is past the selection, abort. | |
297 | + break; | |
298 | + } | |
299 | + } | |
300 | + } catch (JavaModelException e1) { | |
301 | + // Error in unit.getBuffer. Ignore. | |
302 | + } catch (InvalidInputException e2) { | |
303 | + // Error in scanner.getNextToken. Ignore. | |
304 | + } finally { | |
305 | + monitor.worked(1); | |
306 | + } | |
307 | + | |
308 | + if (mTokenString != null) { | |
309 | + // As a literal string, the token should have surrounding quotes. Remove them. | |
310 | + int len = mTokenString.length(); | |
311 | + if (len > 0 && | |
312 | + mTokenString.charAt(0) == '"' && | |
313 | + mTokenString.charAt(len - 1) == '"') { | |
314 | + mTokenString = mTokenString.substring(1, len - 1); | |
315 | + } | |
316 | + // We need a non-empty string literal | |
317 | + if (mTokenString.length() == 0) { | |
318 | + mTokenString = null; | |
319 | + } | |
298 | 320 | } |
321 | + | |
322 | + if (mTokenString == null) { | |
323 | + status.addFatalError("Please select a Java string literal."); | |
324 | + } | |
325 | + | |
299 | 326 | monitor.worked(1); |
327 | + return status.isOK(); | |
328 | + } | |
300 | 329 | |
330 | + /** | |
331 | + * Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit() | |
332 | + * Might not be useful. | |
333 | + * | |
334 | + * On success, advance the monitor by 2. | |
335 | + * | |
336 | + * @return False if caller should abort, true if caller should continue. | |
337 | + */ | |
338 | + private boolean checkSourceFile(IFile file, | |
339 | + RefactoringStatus status, | |
340 | + IProgressMonitor monitor) { | |
301 | 341 | // check whether the source file is in sync |
302 | - if (!res.isSynchronized(IResource.DEPTH_ZERO)) { | |
342 | + if (!file.isSynchronized(IResource.DEPTH_ZERO)) { | |
303 | 343 | status.addFatalError("The file is not synchronized. Please save it first."); |
304 | 344 | return false; |
305 | 345 | } |
306 | 346 | monitor.worked(1); |
307 | 347 | |
308 | 348 | // make sure we can write to it. |
309 | - ResourceAttributes resAttr = res.getResourceAttributes(); | |
310 | - if (mUnit.isReadOnly() || resAttr == null || resAttr.isReadOnly()) { | |
349 | + ResourceAttributes resAttr = file.getResourceAttributes(); | |
350 | + if (resAttr == null || resAttr.isReadOnly()) { | |
311 | 351 | status.addFatalError("The file is read-only, please make it writeable first."); |
312 | 352 | return false; |
313 | 353 | } |