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 org.eclipse.core.resources.IFile;
     20 import org.eclipse.core.resources.IMarker;
     21 import org.eclipse.core.resources.IResource;
     22 import org.eclipse.core.resources.IWorkspaceRoot;
     23 import org.eclipse.core.resources.ResourcesPlugin;
     24 import org.eclipse.core.runtime.CoreException;
     25 import org.eclipse.core.runtime.IPath;
     26 import org.eclipse.core.runtime.Path;
     27 import org.eclipse.jface.text.BadLocationException;
     28 import org.eclipse.jface.text.IDocument;
     29 import org.eclipse.jface.text.IRegion;
     30 import org.eclipse.ui.IEditorInput;
     31 import org.eclipse.ui.IEditorPart;
     32 import org.eclipse.ui.IFileEditorInput;
     33 import org.eclipse.ui.IWorkbenchPage;
     34 import org.eclipse.ui.IWorkbenchWindow;
     35 import org.eclipse.ui.PlatformUI;
     36 import org.eclipse.ui.texteditor.ITextEditor;
     37 
     38 import java.io.File;
     39 import java.util.ArrayList;
     40 import java.util.List;
     41 
     42 
     43 /** Utility methods for ADT */
     44 public class AdtUtils {
     45     /**
     46      * Returns true if the given string ends with the given suffix, using a
     47      * case-insensitive comparison.
     48      *
     49      * @param string the full string to be checked
     50      * @param suffix the suffix to be checked for
     51      * @return true if the string case-insensitively ends with the given suffix
     52      */
     53     public static boolean endsWithIgnoreCase(String string, String suffix) {
     54         return string.regionMatches(true /* ignoreCase */, string.length() - suffix.length(),
     55                 suffix, 0, suffix.length());
     56     }
     57 
     58     /**
     59      * Returns true if the given sequence ends with the given suffix (case
     60      * sensitive).
     61      *
     62      * @param sequence the character sequence to be checked
     63      * @param suffix the suffix to look for
     64      * @return true if the given sequence ends with the given suffix
     65      */
     66     public static boolean endsWith(CharSequence sequence, CharSequence suffix) {
     67         return endsWith(sequence, sequence.length(), suffix);
     68     }
     69 
     70     /**
     71      * Returns true if the given sequence ends at the given offset with the given suffix (case
     72      * sensitive)
     73      *
     74      * @param sequence the character sequence to be checked
     75      * @param endOffset the offset at which the sequence is considered to end
     76      * @param suffix the suffix to look for
     77      * @return true if the given sequence ends with the given suffix
     78      */
     79     public static boolean endsWith(CharSequence sequence, int endOffset, CharSequence suffix) {
     80         if (endOffset < suffix.length()) {
     81             return false;
     82         }
     83 
     84         for (int i = endOffset - 1, j = suffix.length() - 1; j >= 0; i--, j--) {
     85             if (sequence.charAt(i) != suffix.charAt(j)) {
     86                 return false;
     87             }
     88         }
     89 
     90         return true;
     91     }
     92 
     93     /**
     94      * Strips the whitespace from the given string
     95      *
     96      * @param string the string to be cleaned up
     97      * @return the string, without whitespace
     98      */
     99     public static String stripWhitespace(String string) {
    100         StringBuilder sb = new StringBuilder(string.length());
    101         for (int i = 0, n = string.length(); i < n; i++) {
    102             char c = string.charAt(i);
    103             if (!Character.isWhitespace(c)) {
    104                 sb.append(c);
    105             }
    106         }
    107 
    108         return sb.toString();
    109     }
    110 
    111     /**
    112      * Creates a Java class name out of the given string, if possible. For
    113      * example, "My Project" becomes "MyProject", "hello" becomes "Hello",
    114      * "Java's" becomes "Java", and so on.
    115      *
    116      * @param string the string to be massaged into a Java class
    117      * @return the string as a Java class, or null if a class name could not be
    118      *         extracted
    119      */
    120     public static String extractClassName(String string) {
    121         StringBuilder sb = new StringBuilder(string.length());
    122         int n = string.length();
    123 
    124         int i = 0;
    125         for (; i < n; i++) {
    126             char c = Character.toUpperCase(string.charAt(i));
    127             if (Character.isJavaIdentifierStart(c)) {
    128                 sb.append(c);
    129                 i++;
    130                 break;
    131             }
    132         }
    133         if (sb.length() > 0) {
    134             for (; i < n; i++) {
    135                 char c = string.charAt(i);
    136                 if (Character.isJavaIdentifierPart(c)) {
    137                     sb.append(c);
    138                 }
    139             }
    140 
    141             return sb.toString();
    142         }
    143 
    144         return null;
    145     }
    146 
    147     /**
    148      * Strips off the last file extension from the given filename, e.g.
    149      * "foo.backup.diff" will be turned into "foo.backup".
    150      * <p>
    151      * Note that dot files (e.g. ".profile") will be left alone.
    152      *
    153      * @param filename the filename to be stripped
    154      * @return the filename without the last file extension.
    155      */
    156     public static String stripLastExtension(String filename) {
    157         int dotIndex = filename.lastIndexOf('.');
    158         if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently
    159             return filename.substring(0, dotIndex);
    160         } else {
    161             return filename;
    162         }
    163     }
    164 
    165     /**
    166      * Strips off all extensions from the given filename, e.g. "foo.9.png" will
    167      * be turned into "foo".
    168      * <p>
    169      * Note that dot files (e.g. ".profile") will be left alone.
    170      *
    171      * @param filename the filename to be stripped
    172      * @return the filename without any file extensions
    173      */
    174     public static String stripAllExtensions(String filename) {
    175         int dotIndex = filename.indexOf('.');
    176         if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently
    177             return filename.substring(0, dotIndex);
    178         } else {
    179             return filename;
    180         }
    181     }
    182 
    183     /**
    184      * Capitalizes the string, i.e. transforms the initial [a-z] into [A-Z].
    185      * Returns the string unmodified if the first character is not [a-z].
    186      *
    187      * @param str The string to capitalize.
    188      * @return The capitalized string
    189      */
    190     public static String capitalize(String str) {
    191         if (str == null || str.length() < 1 || Character.isUpperCase(str.charAt(0))) {
    192             return str;
    193         }
    194 
    195         StringBuilder sb = new StringBuilder();
    196         sb.append(Character.toUpperCase(str.charAt(0)));
    197         sb.append(str.substring(1));
    198         return sb.toString();
    199     }
    200 
    201     /**
    202      * Computes the edit distance (number of insertions, deletions or substitutions
    203      * to edit one string into the other) between two strings. In particular,
    204      * this will compute the Levenshtein distance.
    205      * <p>
    206      * See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
    207      *
    208      * @param s the first string to compare
    209      * @param t the second string to compare
    210      * @return the edit distance between the two strings
    211      */
    212     public static int editDistance(String s, String t) {
    213         int m = s.length();
    214         int n = t.length();
    215         int[][] d = new int[m + 1][n + 1];
    216         for (int i = 0; i <= m; i++) {
    217             d[i][0] = i;
    218         }
    219         for (int j = 0; j <= n; j++) {
    220             d[0][j] = j;
    221         }
    222         for (int j = 1; j <= n; j++) {
    223             for (int i = 1; i <= m; i++) {
    224                 if (s.charAt(i - 1) == t.charAt(j - 1)) {
    225                     d[i][j] = d[i - 1][j - 1];
    226                 } else {
    227                     int deletion = d[i - 1][j] + 1;
    228                     int insertion = d[i][j - 1] + 1;
    229                     int substitution = d[i - 1][j - 1] + 1;
    230                     d[i][j] = Math.min(deletion, Math.min(insertion, substitution));
    231                 }
    232             }
    233         }
    234 
    235         return d[m][n];
    236     }
    237 
    238     /**
    239      * Returns the current editor (the currently visible and active editor), or null if
    240      * not found
    241      *
    242      * @return the current editor, or null
    243      */
    244     public static IEditorPart getActiveEditor() {
    245         IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    246         if (window != null) {
    247             IWorkbenchPage page = window.getActivePage();
    248             if (page != null) {
    249                 return page.getActiveEditor();
    250             }
    251         }
    252 
    253         return null;
    254     }
    255 
    256     /**
    257      * Returns the current text editor (the currently visible and active editor), or null
    258      * if not found.
    259      *
    260      * @return the current text editor, or null
    261      */
    262     public static ITextEditor getActiveTextEditor() {
    263         IEditorPart editor = getActiveEditor();
    264         if (editor != null) {
    265             if (editor instanceof ITextEditor) {
    266                 return (ITextEditor) editor;
    267             } else {
    268                 return (ITextEditor) editor.getAdapter(ITextEditor.class);
    269             }
    270         }
    271 
    272         return null;
    273     }
    274 
    275     /**
    276      * Returns the file for the current editor, if any.
    277      *
    278      * @return the file for the current editor, or null if none
    279      */
    280     public static IFile getActiveFile() {
    281         IEditorPart editor = getActiveEditor();
    282         if (editor != null) {
    283             IEditorInput input = editor.getEditorInput();
    284             if (input instanceof IFileEditorInput) {
    285                 IFileEditorInput fileInput = (IFileEditorInput) input;
    286                 return fileInput.getFile();
    287             }
    288         }
    289 
    290         return null;
    291     }
    292 
    293     /**
    294      * Returns an absolute path to the given resource
    295      *
    296      * @param resource the resource to look up a path for
    297      * @return an absolute file system path to the resource
    298      */
    299     public static IPath getAbsolutePath(IResource resource) {
    300         IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
    301         IPath workspacePath = workspace.getLocation();
    302         return workspacePath.append(resource.getFullPath());
    303     }
    304 
    305     /**
    306      * Converts a {@link File} to an {@link IFile}, if possible.
    307      *
    308      * @param file a file to be converted
    309      * @return the corresponding {@link IFile}, or null
    310      */
    311     public static IFile fileToIFile(File file) {
    312         IPath filePath = new Path(file.getPath());
    313         return pathToIFile(filePath);
    314     }
    315 
    316     /**
    317      * Converts a {@link IPath} to an {@link IFile}, if possible.
    318      *
    319      * @param path a path to be converted
    320      * @return the corresponding {@link IFile}, or null
    321      */
    322     public static IFile pathToIFile(IPath path) {
    323         IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
    324         IPath workspacePath = workspace.getLocation();
    325         if (workspacePath.isPrefixOf(path)) {
    326             IPath relativePath = path.makeRelativeTo(workspacePath);
    327             IResource member = workspace.findMember(relativePath);
    328             if (member instanceof IFile) {
    329                 return (IFile) member;
    330             }
    331         } else if (path.isAbsolute()) {
    332             return workspace.getFileForLocation(path);
    333         }
    334 
    335         return null;
    336     }
    337 
    338     /**
    339      * Returns all markers in a file/document that fit on the same line as the given offset
    340      *
    341      * @param markerType the marker type
    342      * @param file the file containing the markers
    343      * @param document the document showing the markers
    344      * @param offset the offset to be checked
    345      * @return a list (possibly empty but never null) of matching markers
    346      */
    347     public static List<IMarker> findMarkersOnLine(String markerType,
    348             IFile file, IDocument document, int offset) {
    349         List<IMarker> matchingMarkers = new ArrayList<IMarker>(2);
    350         try {
    351             IMarker[] markers = file.findMarkers(markerType, true, IResource.DEPTH_ZERO);
    352 
    353             // Look for a match on the same line as the caret.
    354             IRegion lineInfo = document.getLineInformationOfOffset(offset);
    355             int lineStart = lineInfo.getOffset();
    356             int lineEnd = lineStart + lineInfo.getLength();
    357             int offsetLine = document.getLineOfOffset(offset);
    358 
    359 
    360             for (IMarker marker : markers) {
    361                 int start = marker.getAttribute(IMarker.CHAR_START, -1);
    362                 int end = marker.getAttribute(IMarker.CHAR_END, -1);
    363                 if (start >= lineStart && start <= lineEnd && end > start) {
    364                     matchingMarkers.add(marker);
    365                 } else if (start == -1 && end == -1) {
    366                     // Some markers don't set character range, they only set the line
    367                     int line = marker.getAttribute(IMarker.LINE_NUMBER, -1);
    368                     if (line == offsetLine + 1) {
    369                         matchingMarkers.add(marker);
    370                     }
    371                 }
    372             }
    373         } catch (CoreException e) {
    374             AdtPlugin.log(e, null);
    375         } catch (BadLocationException e) {
    376             AdtPlugin.log(e, null);
    377         }
    378 
    379         return matchingMarkers;
    380     }
    381 }
    382