Home | History | Annotate | Download | only in adt
      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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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
    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