• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt;
18 
19 import com.android.annotations.NonNull;
20 import com.android.annotations.Nullable;
21 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
22 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper.IProjectFilter;
23 
24 import org.eclipse.core.filesystem.URIUtil;
25 import org.eclipse.core.resources.IFile;
26 import org.eclipse.core.resources.IMarker;
27 import org.eclipse.core.resources.IProject;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.resources.IWorkspace;
30 import org.eclipse.core.resources.IWorkspaceRoot;
31 import org.eclipse.core.resources.ResourcesPlugin;
32 import org.eclipse.core.runtime.CoreException;
33 import org.eclipse.core.runtime.IPath;
34 import org.eclipse.core.runtime.Path;
35 import org.eclipse.jdt.core.IJavaProject;
36 import org.eclipse.jface.text.BadLocationException;
37 import org.eclipse.jface.text.IDocument;
38 import org.eclipse.jface.text.IRegion;
39 import org.eclipse.ui.IEditorInput;
40 import org.eclipse.ui.IEditorPart;
41 import org.eclipse.ui.IFileEditorInput;
42 import org.eclipse.ui.IURIEditorInput;
43 import org.eclipse.ui.IWorkbenchPage;
44 import org.eclipse.ui.IWorkbenchWindow;
45 import org.eclipse.ui.PlatformUI;
46 import org.eclipse.ui.texteditor.ITextEditor;
47 
48 import java.io.File;
49 import java.net.URISyntaxException;
50 import java.net.URL;
51 import java.util.ArrayList;
52 import java.util.List;
53 
54 
55 /** Utility methods for ADT */
56 public class AdtUtils {
57     /**
58      * Returns true if the given string ends with the given suffix, using a
59      * case-insensitive comparison.
60      *
61      * @param string the full string to be checked
62      * @param suffix the suffix to be checked for
63      * @return true if the string case-insensitively ends with the given suffix
64      */
endsWithIgnoreCase(String string, String suffix)65     public static boolean endsWithIgnoreCase(String string, String suffix) {
66         return string.regionMatches(true /* ignoreCase */, string.length() - suffix.length(),
67                 suffix, 0, suffix.length());
68     }
69 
70     /**
71      * Returns true if the given sequence ends with the given suffix (case
72      * sensitive).
73      *
74      * @param sequence the character sequence to be checked
75      * @param suffix the suffix to look for
76      * @return true if the given sequence ends with the given suffix
77      */
endsWith(CharSequence sequence, CharSequence suffix)78     public static boolean endsWith(CharSequence sequence, CharSequence suffix) {
79         return endsWith(sequence, sequence.length(), suffix);
80     }
81 
82     /**
83      * Returns true if the given sequence ends at the given offset with the given suffix (case
84      * sensitive)
85      *
86      * @param sequence the character sequence to be checked
87      * @param endOffset the offset at which the sequence is considered to end
88      * @param suffix the suffix to look for
89      * @return true if the given sequence ends with the given suffix
90      */
endsWith(CharSequence sequence, int endOffset, CharSequence suffix)91     public static boolean endsWith(CharSequence sequence, int endOffset, CharSequence suffix) {
92         if (endOffset < suffix.length()) {
93             return false;
94         }
95 
96         for (int i = endOffset - 1, j = suffix.length() - 1; j >= 0; i--, j--) {
97             if (sequence.charAt(i) != suffix.charAt(j)) {
98                 return false;
99             }
100         }
101 
102         return true;
103     }
104 
105     /**
106      * Returns true if the given string starts with the given prefix, using a
107      * case-insensitive comparison.
108      *
109      * @param string the full string to be checked
110      * @param prefix the prefix to be checked for
111      * @return true if the string case-insensitively starts with the given prefix
112      */
startsWithIgnoreCase(String string, String prefix)113     public static boolean startsWithIgnoreCase(String string, String prefix) {
114         return string.regionMatches(true /* ignoreCase */, 0, prefix, 0, prefix.length());
115     }
116 
117     /**
118      * Returns true if the given string starts at the given offset with the
119      * given prefix, case insensitively.
120      *
121      * @param string the full string to be checked
122      * @param offset the offset in the string to start looking
123      * @param prefix the prefix to be checked for
124      * @return true if the string case-insensitively starts at the given offset
125      *         with the given prefix
126      */
startsWith(String string, int offset, String prefix)127     public static boolean startsWith(String string, int offset, String prefix) {
128         return string.regionMatches(true /* ignoreCase */, offset, prefix, 0, prefix.length());
129     }
130 
131     /**
132      * Strips the whitespace from the given string
133      *
134      * @param string the string to be cleaned up
135      * @return the string, without whitespace
136      */
stripWhitespace(String string)137     public static String stripWhitespace(String string) {
138         StringBuilder sb = new StringBuilder(string.length());
139         for (int i = 0, n = string.length(); i < n; i++) {
140             char c = string.charAt(i);
141             if (!Character.isWhitespace(c)) {
142                 sb.append(c);
143             }
144         }
145 
146         return sb.toString();
147     }
148 
149     /**
150      * Creates a Java class name out of the given string, if possible. For
151      * example, "My Project" becomes "MyProject", "hello" becomes "Hello",
152      * "Java's" becomes "Java", and so on.
153      *
154      * @param string the string to be massaged into a Java class
155      * @return the string as a Java class, or null if a class name could not be
156      *         extracted
157      */
extractClassName(String string)158     public static String extractClassName(String string) {
159         StringBuilder sb = new StringBuilder(string.length());
160         int n = string.length();
161 
162         int i = 0;
163         for (; i < n; i++) {
164             char c = Character.toUpperCase(string.charAt(i));
165             if (Character.isJavaIdentifierStart(c)) {
166                 sb.append(c);
167                 i++;
168                 break;
169             }
170         }
171         if (sb.length() > 0) {
172             for (; i < n; i++) {
173                 char c = string.charAt(i);
174                 if (Character.isJavaIdentifierPart(c)) {
175                     sb.append(c);
176                 }
177             }
178 
179             return sb.toString();
180         }
181 
182         return null;
183     }
184 
185     /**
186      * Strips off the last file extension from the given filename, e.g.
187      * "foo.backup.diff" will be turned into "foo.backup".
188      * <p>
189      * Note that dot files (e.g. ".profile") will be left alone.
190      *
191      * @param filename the filename to be stripped
192      * @return the filename without the last file extension.
193      */
stripLastExtension(String filename)194     public static String stripLastExtension(String filename) {
195         int dotIndex = filename.lastIndexOf('.');
196         if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently
197             return filename.substring(0, dotIndex);
198         } else {
199             return filename;
200         }
201     }
202 
203     /**
204      * Strips off all extensions from the given filename, e.g. "foo.9.png" will
205      * be turned into "foo".
206      * <p>
207      * Note that dot files (e.g. ".profile") will be left alone.
208      *
209      * @param filename the filename to be stripped
210      * @return the filename without any file extensions
211      */
stripAllExtensions(String filename)212     public static String stripAllExtensions(String filename) {
213         int dotIndex = filename.indexOf('.');
214         if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently
215             return filename.substring(0, dotIndex);
216         } else {
217             return filename;
218         }
219     }
220 
221     /**
222      * Capitalizes the string, i.e. transforms the initial [a-z] into [A-Z].
223      * Returns the string unmodified if the first character is not [a-z].
224      *
225      * @param str The string to capitalize.
226      * @return The capitalized string
227      */
capitalize(String str)228     public static String capitalize(String str) {
229         if (str == null || str.length() < 1 || Character.isUpperCase(str.charAt(0))) {
230             return str;
231         }
232 
233         StringBuilder sb = new StringBuilder();
234         sb.append(Character.toUpperCase(str.charAt(0)));
235         sb.append(str.substring(1));
236         return sb.toString();
237     }
238 
239     /**
240      * Returns the current editor (the currently visible and active editor), or null if
241      * not found
242      *
243      * @return the current editor, or null
244      */
getActiveEditor()245     public static IEditorPart getActiveEditor() {
246         IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
247         if (window != null) {
248             IWorkbenchPage page = window.getActivePage();
249             if (page != null) {
250                 return page.getActiveEditor();
251             }
252         }
253 
254         return null;
255     }
256 
257     /**
258      * Returns the current text editor (the currently visible and active editor), or null
259      * if not found.
260      *
261      * @return the current text editor, or null
262      */
getActiveTextEditor()263     public static ITextEditor getActiveTextEditor() {
264         IEditorPart editor = getActiveEditor();
265         if (editor != null) {
266             if (editor instanceof ITextEditor) {
267                 return (ITextEditor) editor;
268             } else {
269                 return (ITextEditor) editor.getAdapter(ITextEditor.class);
270             }
271         }
272 
273         return null;
274     }
275 
276     /**
277      * Attempts to convert the given {@link URL} into a {@link File}.
278      *
279      * @param url the {@link URL} to be converted
280      * @return the corresponding {@link File}, which may not exist
281      */
282     @NonNull
getFile(@onNull URL url)283     public static File getFile(@NonNull URL url) {
284         try {
285             // First try URL.toURI(): this will work for URLs that contain %20 for spaces etc.
286             // Unfortunately, it *doesn't* work for "broken" URLs where the URL contains
287             // spaces, which is often the case.
288             return new File(url.toURI());
289         } catch (URISyntaxException e) {
290             // ...so as a fallback, go to the old url.getPath() method, which handles space paths.
291             return new File(url.getPath());
292         }
293     }
294 
295     /**
296      * Returns the file for the current editor, if any.
297      *
298      * @return the file for the current editor, or null if none
299      */
getActiveFile()300     public static IFile getActiveFile() {
301         IEditorPart editor = getActiveEditor();
302         if (editor != null) {
303             IEditorInput input = editor.getEditorInput();
304             if (input instanceof IFileEditorInput) {
305                 IFileEditorInput fileInput = (IFileEditorInput) input;
306                 return fileInput.getFile();
307             }
308         }
309 
310         return null;
311     }
312 
313     /**
314      * Returns an absolute path to the given resource
315      *
316      * @param resource the resource to look up a path for
317      * @return an absolute file system path to the resource
318      */
getAbsolutePath(IResource resource)319     public static IPath getAbsolutePath(IResource resource) {
320         IPath location = resource.getRawLocation();
321         if (location != null) {
322             return location.makeAbsolute();
323         } else {
324             IWorkspace workspace = ResourcesPlugin.getWorkspace();
325             IWorkspaceRoot root = workspace.getRoot();
326             IPath workspacePath = root.getLocation();
327             return workspacePath.append(resource.getFullPath());
328         }
329     }
330 
331     /**
332      * Converts a {@link File} to an {@link IFile}, if possible.
333      *
334      * @param file a file to be converted
335      * @return the corresponding {@link IFile}, or null
336      */
fileToIFile(File file)337     public static IFile fileToIFile(File file) {
338         if (!file.isAbsolute()) {
339             file = file.getAbsoluteFile();
340         }
341 
342         IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
343         IFile[] files = workspace.findFilesForLocationURI(file.toURI());
344         if (files.length > 0) {
345             return files[0];
346         }
347 
348         IPath filePath = new Path(file.getPath());
349         return pathToIFile(filePath);
350     }
351 
352     /**
353      * Converts a {@link File} to an {@link IResource}, if possible.
354      *
355      * @param file a file to be converted
356      * @return the corresponding {@link IResource}, or null
357      */
fileToResource(File file)358     public static IResource fileToResource(File file) {
359         if (!file.isAbsolute()) {
360             file = file.getAbsoluteFile();
361         }
362 
363         IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
364         IFile[] files = workspace.findFilesForLocationURI(file.toURI());
365         if (files.length > 0) {
366             return files[0];
367         }
368 
369         IPath filePath = new Path(file.getPath());
370         return pathToResource(filePath);
371     }
372 
373     /**
374      * Converts a {@link IPath} to an {@link IFile}, if possible.
375      *
376      * @param path a path to be converted
377      * @return the corresponding {@link IFile}, or null
378      */
pathToIFile(IPath path)379     public static IFile pathToIFile(IPath path) {
380         IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
381 
382         IFile[] files = workspace.findFilesForLocationURI(URIUtil.toURI(path.makeAbsolute()));
383         if (files.length > 0) {
384             return files[0];
385         }
386 
387         IPath workspacePath = workspace.getLocation();
388         if (workspacePath.isPrefixOf(path)) {
389             IPath relativePath = path.makeRelativeTo(workspacePath);
390             IResource member = workspace.findMember(relativePath);
391             if (member instanceof IFile) {
392                 return (IFile) member;
393             }
394         } else if (path.isAbsolute()) {
395             return workspace.getFileForLocation(path);
396         }
397 
398         return null;
399     }
400 
401     /**
402      * Converts a {@link IPath} to an {@link IResource}, if possible.
403      *
404      * @param path a path to be converted
405      * @return the corresponding {@link IResource}, or null
406      */
pathToResource(IPath path)407     public static IResource pathToResource(IPath path) {
408         IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
409 
410         IFile[] files = workspace.findFilesForLocationURI(URIUtil.toURI(path.makeAbsolute()));
411         if (files.length > 0) {
412             return files[0];
413         }
414 
415         IPath workspacePath = workspace.getLocation();
416         if (workspacePath.isPrefixOf(path)) {
417             IPath relativePath = path.makeRelativeTo(workspacePath);
418             return workspace.findMember(relativePath);
419         } else if (path.isAbsolute()) {
420             return workspace.getFileForLocation(path);
421         }
422 
423         return null;
424     }
425 
426     /**
427      * Returns all markers in a file/document that fit on the same line as the given offset
428      *
429      * @param markerType the marker type
430      * @param file the file containing the markers
431      * @param document the document showing the markers
432      * @param offset the offset to be checked
433      * @return a list (possibly empty but never null) of matching markers
434      */
435     @NonNull
findMarkersOnLine( @onNull String markerType, @NonNull IResource file, @NonNull IDocument document, int offset)436     public static List<IMarker> findMarkersOnLine(
437             @NonNull String markerType,
438             @NonNull IResource file,
439             @NonNull IDocument document,
440             int offset) {
441         List<IMarker> matchingMarkers = new ArrayList<IMarker>(2);
442         try {
443             IMarker[] markers = file.findMarkers(markerType, true, IResource.DEPTH_ZERO);
444 
445             // Look for a match on the same line as the caret.
446             IRegion lineInfo = document.getLineInformationOfOffset(offset);
447             int lineStart = lineInfo.getOffset();
448             int lineEnd = lineStart + lineInfo.getLength();
449             int offsetLine = document.getLineOfOffset(offset);
450 
451 
452             for (IMarker marker : markers) {
453                 int start = marker.getAttribute(IMarker.CHAR_START, -1);
454                 int end = marker.getAttribute(IMarker.CHAR_END, -1);
455                 if (start >= lineStart && start <= lineEnd && end > start) {
456                     matchingMarkers.add(marker);
457                 } else if (start == -1 && end == -1) {
458                     // Some markers don't set character range, they only set the line
459                     int line = marker.getAttribute(IMarker.LINE_NUMBER, -1);
460                     if (line == offsetLine + 1) {
461                         matchingMarkers.add(marker);
462                     }
463                 }
464             }
465         } catch (CoreException e) {
466             AdtPlugin.log(e, null);
467         } catch (BadLocationException e) {
468             AdtPlugin.log(e, null);
469         }
470 
471         return matchingMarkers;
472     }
473 
474     /**
475      * Returns the available and open Android projects
476      *
477      * @return the available and open Android projects, never null
478      */
479     @NonNull
getOpenAndroidProjects()480     public static IJavaProject[] getOpenAndroidProjects() {
481         return BaseProjectHelper.getAndroidProjects(new IProjectFilter() {
482             @Override
483             public boolean accept(IProject project) {
484                 return project.isAccessible();
485             }
486         });
487     }
488 
489     /**
490      * Returns a unique project name, based on the given {@code base} file name
491      * possibly with a {@code conjunction} and a new number behind it to ensure
492      * that the project name is unique. For example,
493      * {@code getUniqueProjectName("project", "_")} will return
494      * {@code "project"} if that name does not already exist, and if it does, it
495      * will return {@code "project_2"}.
496      *
497      * @param base the base name to use, such as "foo"
498      * @param conjunction a string to insert between the base name and the
499      *            number.
500      * @return a unique project name based on the given base and conjunction
501      */
502     public static String getUniqueProjectName(String base, String conjunction) {
503         // We're using all workspace projects here rather than just open Android project
504         // via getOpenAndroidProjects because the name cannot conflict with non-Android
505         // or closed projects either
506         IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
507         IProject[] projects = workspaceRoot.getProjects();
508 
509         for (int i = 1; i < 1000; i++) {
510             String name = i == 1 ? base : base + conjunction + Integer.toString(i);
511             boolean found = false;
512             for (IProject project : projects) {
513                 if (project.getName().equals(name)) {
514                     found = true;
515                     break;
516                 }
517             }
518             if (!found) {
519                 return name;
520             }
521         }
522 
523         return base;
524     }
525 
526     /**
527      * Returns the name of the parent folder for the given editor input
528      *
529      * @param editorInput the editor input to check
530      * @return the parent folder, which is never null but may be ""
531      */
532     @NonNull
533     public static String getParentFolderName(@Nullable IEditorInput editorInput) {
534         if (editorInput instanceof IFileEditorInput) {
535              IFile file = ((IFileEditorInput) editorInput).getFile();
536              return file.getParent().getName();
537         }
538 
539         if (editorInput instanceof IURIEditorInput) {
540             IURIEditorInput urlEditorInput = (IURIEditorInput) editorInput;
541             String path = urlEditorInput.getURI().toString();
542             int lastIndex = path.lastIndexOf('/');
543             if (lastIndex != -1) {
544                 int lastLastIndex = path.lastIndexOf('/', lastIndex - 1);
545                 if (lastLastIndex != -1) {
546                     return path.substring(lastLastIndex + 1, lastIndex);
547                 }
548             }
549         }
550 
551         return "";
552     }
553 }
554