| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/org/documents/epl-v10.php |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ide.eclipse.adt; |
| |
| import static com.android.SdkConstants.TOOLS_PREFIX; |
| import static com.android.SdkConstants.TOOLS_URI; |
| import static org.eclipse.ui.IWorkbenchPage.MATCH_INPUT; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.sdklib.SdkVersionInfo; |
| import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; |
| import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; |
| import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; |
| import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; |
| import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper.IProjectFilter; |
| import com.android.ide.eclipse.adt.internal.sdk.Sdk; |
| import com.android.resources.ResourceFolderType; |
| import com.android.resources.ResourceType; |
| import com.android.sdklib.AndroidVersion; |
| import com.android.sdklib.IAndroidTarget; |
| import com.android.sdklib.repository.PkgProps; |
| import com.android.utils.XmlUtils; |
| import com.google.common.io.ByteStreams; |
| import com.google.common.io.Closeables; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBuffer; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.filebuffers.LocationKind; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorReference; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IURIEditorInput; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.editors.text.TextFileDocumentProvider; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.texteditor.IDocumentProvider; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| import java.io.File; |
| import java.io.InputStream; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| |
| |
| /** Utility methods for ADT */ |
| @SuppressWarnings("restriction") // WST API |
| public class AdtUtils { |
| /** |
| * Creates a Java class name out of the given string, if possible. For |
| * example, "My Project" becomes "MyProject", "hello" becomes "Hello", |
| * "Java's" becomes "Java", and so on. |
| * |
| * @param string the string to be massaged into a Java class |
| * @return the string as a Java class, or null if a class name could not be |
| * extracted |
| */ |
| @Nullable |
| public static String extractClassName(@NonNull String string) { |
| StringBuilder sb = new StringBuilder(string.length()); |
| int n = string.length(); |
| |
| int i = 0; |
| for (; i < n; i++) { |
| char c = Character.toUpperCase(string.charAt(i)); |
| if (Character.isJavaIdentifierStart(c)) { |
| sb.append(c); |
| i++; |
| break; |
| } |
| } |
| if (sb.length() > 0) { |
| for (; i < n; i++) { |
| char c = string.charAt(i); |
| if (Character.isJavaIdentifierPart(c)) { |
| sb.append(c); |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Strips off the last file extension from the given filename, e.g. |
| * "foo.backup.diff" will be turned into "foo.backup". |
| * <p> |
| * Note that dot files (e.g. ".profile") will be left alone. |
| * |
| * @param filename the filename to be stripped |
| * @return the filename without the last file extension. |
| */ |
| public static String stripLastExtension(String filename) { |
| int dotIndex = filename.lastIndexOf('.'); |
| if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently |
| return filename.substring(0, dotIndex); |
| } else { |
| return filename; |
| } |
| } |
| |
| /** |
| * Strips off all extensions from the given filename, e.g. "foo.9.png" will |
| * be turned into "foo". |
| * <p> |
| * Note that dot files (e.g. ".profile") will be left alone. |
| * |
| * @param filename the filename to be stripped |
| * @return the filename without any file extensions |
| */ |
| public static String stripAllExtensions(String filename) { |
| int dotIndex = filename.indexOf('.'); |
| if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently |
| return filename.substring(0, dotIndex); |
| } else { |
| return filename; |
| } |
| } |
| |
| /** |
| * Strips the given suffix from the given string, provided that the string ends with |
| * the suffix. |
| * |
| * @param string the full string to strip from |
| * @param suffix the suffix to strip out |
| * @return the string without the suffix at the end |
| */ |
| public static String stripSuffix(@NonNull String string, @NonNull String suffix) { |
| if (string.endsWith(suffix)) { |
| return string.substring(0, string.length() - suffix.length()); |
| } |
| |
| return string; |
| } |
| |
| /** |
| * Capitalizes the string, i.e. transforms the initial [a-z] into [A-Z]. |
| * Returns the string unmodified if the first character is not [a-z]. |
| * |
| * @param str The string to capitalize. |
| * @return The capitalized string |
| */ |
| public static String capitalize(String str) { |
| if (str == null || str.length() < 1 || Character.isUpperCase(str.charAt(0))) { |
| return str; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append(Character.toUpperCase(str.charAt(0))); |
| sb.append(str.substring(1)); |
| return sb.toString(); |
| } |
| |
| /** |
| * Converts a CamelCase word into an underlined_word |
| * |
| * @param string the CamelCase version of the word |
| * @return the underlined version of the word |
| */ |
| public static String camelCaseToUnderlines(String string) { |
| if (string.isEmpty()) { |
| return string; |
| } |
| |
| StringBuilder sb = new StringBuilder(2 * string.length()); |
| int n = string.length(); |
| boolean lastWasUpperCase = Character.isUpperCase(string.charAt(0)); |
| for (int i = 0; i < n; i++) { |
| char c = string.charAt(i); |
| boolean isUpperCase = Character.isUpperCase(c); |
| if (isUpperCase && !lastWasUpperCase) { |
| sb.append('_'); |
| } |
| lastWasUpperCase = isUpperCase; |
| c = Character.toLowerCase(c); |
| sb.append(c); |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Converts an underlined_word into a CamelCase word |
| * |
| * @param string the underlined word to convert |
| * @return the CamelCase version of the word |
| */ |
| public static String underlinesToCamelCase(String string) { |
| StringBuilder sb = new StringBuilder(string.length()); |
| int n = string.length(); |
| |
| int i = 0; |
| boolean upcaseNext = true; |
| for (; i < n; i++) { |
| char c = string.charAt(i); |
| if (c == '_') { |
| upcaseNext = true; |
| } else { |
| if (upcaseNext) { |
| c = Character.toUpperCase(c); |
| } |
| upcaseNext = false; |
| sb.append(c); |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Returns the current editor (the currently visible and active editor), or null if |
| * not found |
| * |
| * @return the current editor, or null |
| */ |
| public static IEditorPart getActiveEditor() { |
| IWorkbenchWindow window = getActiveWorkbenchWindow(); |
| if (window != null) { |
| IWorkbenchPage page = window.getActivePage(); |
| if (page != null) { |
| return page.getActiveEditor(); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the current active workbench, or null if not found |
| * |
| * @return the current window, or null |
| */ |
| @Nullable |
| public static IWorkbenchWindow getActiveWorkbenchWindow() { |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); |
| if (window == null) { |
| IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); |
| if (windows.length > 0) { |
| window = windows[0]; |
| } |
| } |
| |
| return window; |
| } |
| |
| /** |
| * Returns the current active workbench page, or null if not found |
| * |
| * @return the current page, or null |
| */ |
| @Nullable |
| public static IWorkbenchPage getActiveWorkbenchPage() { |
| IWorkbenchWindow window = getActiveWorkbenchWindow(); |
| if (window != null) { |
| IWorkbenchPage page = window.getActivePage(); |
| if (page == null) { |
| IWorkbenchPage[] pages = window.getPages(); |
| if (pages.length > 0) { |
| page = pages[0]; |
| } |
| } |
| |
| return page; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the current active workbench part, or null if not found |
| * |
| * @return the current active workbench part, or null |
| */ |
| @Nullable |
| public static IWorkbenchPart getActivePart() { |
| IWorkbenchWindow window = getActiveWorkbenchWindow(); |
| if (window != null) { |
| IWorkbenchPage activePage = window.getActivePage(); |
| if (activePage != null) { |
| return activePage.getActivePart(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the current text editor (the currently visible and active editor), or null |
| * if not found. |
| * |
| * @return the current text editor, or null |
| */ |
| public static ITextEditor getActiveTextEditor() { |
| IEditorPart editor = getActiveEditor(); |
| if (editor != null) { |
| if (editor instanceof ITextEditor) { |
| return (ITextEditor) editor; |
| } else { |
| return (ITextEditor) editor.getAdapter(ITextEditor.class); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Looks through the open editors and returns the editors that have the |
| * given file as input. |
| * |
| * @param file the file to search for |
| * @param restore whether editors should be restored (if they have an open |
| * tab, but the editor hasn't been restored since the most recent |
| * IDE start yet |
| * @return a collection of editors |
| */ |
| @NonNull |
| public static Collection<IEditorPart> findEditorsFor(@NonNull IFile file, boolean restore) { |
| FileEditorInput input = new FileEditorInput(file); |
| List<IEditorPart> result = null; |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); |
| for (IWorkbenchWindow window : windows) { |
| IWorkbenchPage[] pages = window.getPages(); |
| for (IWorkbenchPage page : pages) { |
| IEditorReference[] editors = page.findEditors(input, null, MATCH_INPUT); |
| if (editors != null) { |
| for (IEditorReference reference : editors) { |
| IEditorPart editor = reference.getEditor(restore); |
| if (editor != null) { |
| if (result == null) { |
| result = new ArrayList<IEditorPart>(); |
| } |
| result.add(editor); |
| } |
| } |
| } |
| } |
| } |
| |
| if (result == null) { |
| return Collections.emptyList(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Attempts to convert the given {@link URL} into a {@link File}. |
| * |
| * @param url the {@link URL} to be converted |
| * @return the corresponding {@link File}, which may not exist |
| */ |
| @NonNull |
| public static File getFile(@NonNull URL url) { |
| try { |
| // First try URL.toURI(): this will work for URLs that contain %20 for spaces etc. |
| // Unfortunately, it *doesn't* work for "broken" URLs where the URL contains |
| // spaces, which is often the case. |
| return new File(url.toURI()); |
| } catch (URISyntaxException e) { |
| // ...so as a fallback, go to the old url.getPath() method, which handles space paths. |
| return new File(url.getPath()); |
| } |
| } |
| |
| /** |
| * Returns the file for the current editor, if any. |
| * |
| * @return the file for the current editor, or null if none |
| */ |
| public static IFile getActiveFile() { |
| IEditorPart editor = getActiveEditor(); |
| if (editor != null) { |
| IEditorInput input = editor.getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| IFileEditorInput fileInput = (IFileEditorInput) input; |
| return fileInput.getFile(); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns an absolute path to the given resource |
| * |
| * @param resource the resource to look up a path for |
| * @return an absolute file system path to the resource |
| */ |
| @NonNull |
| public static IPath getAbsolutePath(@NonNull IResource resource) { |
| IPath location = resource.getRawLocation(); |
| if (location != null) { |
| return location.makeAbsolute(); |
| } else { |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| IWorkspaceRoot root = workspace.getRoot(); |
| IPath workspacePath = root.getLocation(); |
| return workspacePath.append(resource.getFullPath()); |
| } |
| } |
| |
| /** |
| * Converts a workspace-relative path to an absolute file path |
| * |
| * @param path the workspace-relative path to convert |
| * @return the corresponding absolute file in the file system |
| */ |
| @NonNull |
| public static File workspacePathToFile(@NonNull IPath path) { |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IResource res = root.findMember(path); |
| if (res != null) { |
| IPath location = res.getLocation(); |
| if (location != null) { |
| return location.toFile(); |
| } |
| return root.getLocation().append(path).toFile(); |
| } |
| |
| return path.toFile(); |
| } |
| |
| /** |
| * Converts a {@link File} to an {@link IFile}, if possible. |
| * |
| * @param file a file to be converted |
| * @return the corresponding {@link IFile}, or null |
| */ |
| public static IFile fileToIFile(File file) { |
| if (!file.isAbsolute()) { |
| file = file.getAbsoluteFile(); |
| } |
| |
| IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); |
| IFile[] files = workspace.findFilesForLocationURI(file.toURI()); |
| if (files.length > 0) { |
| return files[0]; |
| } |
| |
| IPath filePath = new Path(file.getPath()); |
| return pathToIFile(filePath); |
| } |
| |
| /** |
| * Converts a {@link File} to an {@link IResource}, if possible. |
| * |
| * @param file a file to be converted |
| * @return the corresponding {@link IResource}, or null |
| */ |
| public static IResource fileToResource(File file) { |
| if (!file.isAbsolute()) { |
| file = file.getAbsoluteFile(); |
| } |
| |
| IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); |
| IFile[] files = workspace.findFilesForLocationURI(file.toURI()); |
| if (files.length > 0) { |
| return files[0]; |
| } |
| |
| IPath filePath = new Path(file.getPath()); |
| return pathToResource(filePath); |
| } |
| |
| /** |
| * Converts a {@link IPath} to an {@link IFile}, if possible. |
| * |
| * @param path a path to be converted |
| * @return the corresponding {@link IFile}, or null |
| */ |
| public static IFile pathToIFile(IPath path) { |
| IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); |
| |
| IFile[] files = workspace.findFilesForLocationURI(URIUtil.toURI(path.makeAbsolute())); |
| if (files.length > 0) { |
| return files[0]; |
| } |
| |
| IPath workspacePath = workspace.getLocation(); |
| if (workspacePath.isPrefixOf(path)) { |
| IPath relativePath = path.makeRelativeTo(workspacePath); |
| IResource member = workspace.findMember(relativePath); |
| if (member instanceof IFile) { |
| return (IFile) member; |
| } |
| } else if (path.isAbsolute()) { |
| return workspace.getFileForLocation(path); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Converts a {@link IPath} to an {@link IResource}, if possible. |
| * |
| * @param path a path to be converted |
| * @return the corresponding {@link IResource}, or null |
| */ |
| public static IResource pathToResource(IPath path) { |
| IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); |
| |
| IFile[] files = workspace.findFilesForLocationURI(URIUtil.toURI(path.makeAbsolute())); |
| if (files.length > 0) { |
| return files[0]; |
| } |
| |
| IPath workspacePath = workspace.getLocation(); |
| if (workspacePath.isPrefixOf(path)) { |
| IPath relativePath = path.makeRelativeTo(workspacePath); |
| return workspace.findMember(relativePath); |
| } else if (path.isAbsolute()) { |
| return workspace.getFileForLocation(path); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns all markers in a file/document that fit on the same line as the given offset |
| * |
| * @param markerType the marker type |
| * @param file the file containing the markers |
| * @param document the document showing the markers |
| * @param offset the offset to be checked |
| * @return a list (possibly empty but never null) of matching markers |
| */ |
| @NonNull |
| public static List<IMarker> findMarkersOnLine( |
| @NonNull String markerType, |
| @NonNull IResource file, |
| @NonNull IDocument document, |
| int offset) { |
| List<IMarker> matchingMarkers = new ArrayList<IMarker>(2); |
| try { |
| IMarker[] markers = file.findMarkers(markerType, true, IResource.DEPTH_ZERO); |
| |
| // Look for a match on the same line as the caret. |
| IRegion lineInfo = document.getLineInformationOfOffset(offset); |
| int lineStart = lineInfo.getOffset(); |
| int lineEnd = lineStart + lineInfo.getLength(); |
| int offsetLine = document.getLineOfOffset(offset); |
| |
| |
| for (IMarker marker : markers) { |
| int start = marker.getAttribute(IMarker.CHAR_START, -1); |
| int end = marker.getAttribute(IMarker.CHAR_END, -1); |
| if (start >= lineStart && start <= lineEnd && end > start) { |
| matchingMarkers.add(marker); |
| } else if (start == -1 && end == -1) { |
| // Some markers don't set character range, they only set the line |
| int line = marker.getAttribute(IMarker.LINE_NUMBER, -1); |
| if (line == offsetLine + 1) { |
| matchingMarkers.add(marker); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| AdtPlugin.log(e, null); |
| } catch (BadLocationException e) { |
| AdtPlugin.log(e, null); |
| } |
| |
| return matchingMarkers; |
| } |
| |
| /** |
| * Returns the available and open Android projects |
| * |
| * @return the available and open Android projects, never null |
| */ |
| @NonNull |
| public static IJavaProject[] getOpenAndroidProjects() { |
| return BaseProjectHelper.getAndroidProjects(new IProjectFilter() { |
| @Override |
| public boolean accept(IProject project) { |
| return project.isAccessible(); |
| } |
| }); |
| } |
| |
| /** |
| * Returns a unique project name, based on the given {@code base} file name |
| * possibly with a {@code conjunction} and a new number behind it to ensure |
| * that the project name is unique. For example, |
| * {@code getUniqueProjectName("project", "_")} will return |
| * {@code "project"} if that name does not already exist, and if it does, it |
| * will return {@code "project_2"}. |
| * |
| * @param base the base name to use, such as "foo" |
| * @param conjunction a string to insert between the base name and the |
| * number. |
| * @return a unique project name based on the given base and conjunction |
| */ |
| public static String getUniqueProjectName(String base, String conjunction) { |
| // We're using all workspace projects here rather than just open Android project |
| // via getOpenAndroidProjects because the name cannot conflict with non-Android |
| // or closed projects either |
| IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| IProject[] projects = workspaceRoot.getProjects(); |
| |
| for (int i = 1; i < 1000; i++) { |
| String name = i == 1 ? base : base + conjunction + Integer.toString(i); |
| boolean found = false; |
| for (IProject project : projects) { |
| // Need to make case insensitive comparison, since otherwise we can hit |
| // org.eclipse.core.internal.resources.ResourceException: |
| // A resource exists with a different case: '/test'. |
| if (project.getName().equalsIgnoreCase(name)) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| return name; |
| } |
| } |
| |
| return base; |
| } |
| |
| /** |
| * Returns the name of the parent folder for the given editor input |
| * |
| * @param editorInput the editor input to check |
| * @return the parent folder, which is never null but may be "" |
| */ |
| @NonNull |
| public static String getParentFolderName(@Nullable IEditorInput editorInput) { |
| if (editorInput instanceof IFileEditorInput) { |
| IFile file = ((IFileEditorInput) editorInput).getFile(); |
| return file.getParent().getName(); |
| } |
| |
| if (editorInput instanceof IURIEditorInput) { |
| IURIEditorInput urlEditorInput = (IURIEditorInput) editorInput; |
| String path = urlEditorInput.getURI().toString(); |
| int lastIndex = path.lastIndexOf('/'); |
| if (lastIndex != -1) { |
| int lastLastIndex = path.lastIndexOf('/', lastIndex - 1); |
| if (lastLastIndex != -1) { |
| return path.substring(lastLastIndex + 1, lastIndex); |
| } |
| } |
| } |
| |
| return ""; |
| } |
| |
| /** |
| * Returns the XML editor for the given editor part |
| * |
| * @param part the editor part to look up the editor for |
| * @return the editor or null if this part is not an XML editor |
| */ |
| @Nullable |
| public static AndroidXmlEditor getXmlEditor(@NonNull IEditorPart part) { |
| if (part instanceof AndroidXmlEditor) { |
| return (AndroidXmlEditor) part; |
| } else if (part instanceof GraphicalEditorPart) { |
| ((GraphicalEditorPart) part).getEditorDelegate().getEditor(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Sets the given tools: attribute in the given XML editor document, adding |
| * the tools name space declaration if necessary, formatting the affected |
| * document region, and optionally comma-appending to an existing value and |
| * optionally opening and revealing the attribute. |
| * |
| * @param editor the associated editor |
| * @param element the associated element |
| * @param description the description of the attribute (shown in the undo |
| * event) |
| * @param name the name of the attribute |
| * @param value the attribute value |
| * @param reveal if true, open the editor and select the given attribute |
| * node |
| * @param appendValue if true, add this value as a comma separated value to |
| * the existing attribute value, if any |
| */ |
| public static void setToolsAttribute( |
| @NonNull final AndroidXmlEditor editor, |
| @NonNull final Element element, |
| @NonNull final String description, |
| @NonNull final String name, |
| @Nullable final String value, |
| final boolean reveal, |
| final boolean appendValue) { |
| editor.wrapUndoEditXmlModel(description, new Runnable() { |
| @Override |
| public void run() { |
| String prefix = XmlUtils.lookupNamespacePrefix(element, TOOLS_URI, null, true); |
| if (prefix == null) { |
| // Add in new prefix... |
| prefix = XmlUtils.lookupNamespacePrefix(element, |
| TOOLS_URI, TOOLS_PREFIX, true /*create*/); |
| if (value != null) { |
| // ...and ensure that the header is formatted such that |
| // the XML namespace declaration is placed in the right |
| // position and wrapping is applied etc. |
| editor.scheduleNodeReformat(editor.getUiRootNode(), |
| true /*attributesOnly*/); |
| } |
| } |
| |
| String v = value; |
| if (appendValue && v != null) { |
| String prev = element.getAttributeNS(TOOLS_URI, name); |
| if (prev.length() > 0) { |
| v = prev + ',' + value; |
| } |
| } |
| |
| // Use the non-namespace form of set attribute since we can't |
| // reference the namespace until the model has been reloaded |
| if (v != null) { |
| element.setAttribute(prefix + ':' + name, v); |
| } else { |
| element.removeAttribute(prefix + ':' + name); |
| } |
| |
| UiElementNode rootUiNode = editor.getUiRootNode(); |
| if (rootUiNode != null && v != null) { |
| final UiElementNode uiNode = rootUiNode.findXmlNode(element); |
| if (uiNode != null) { |
| editor.scheduleNodeReformat(uiNode, true /*attributesOnly*/); |
| |
| if (reveal) { |
| // Update editor selection after format |
| Display display = AdtPlugin.getDisplay(); |
| if (display != null) { |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| Node xmlNode = uiNode.getXmlNode(); |
| Attr attribute = ((Element) xmlNode).getAttributeNodeNS( |
| TOOLS_URI, name); |
| if (attribute instanceof IndexedRegion) { |
| IndexedRegion region = (IndexedRegion) attribute; |
| editor.getStructuredTextEditor().selectAndReveal( |
| region.getStartOffset(), region.getLength()); |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Returns a string label for the given target, of the form |
| * "API 16: Android 4.1 (Jelly Bean)". |
| * |
| * @param target the target to generate a string from |
| * @return a suitable display string |
| */ |
| @NonNull |
| public static String getTargetLabel(@NonNull IAndroidTarget target) { |
| if (target.isPlatform()) { |
| AndroidVersion version = target.getVersion(); |
| String codename = target.getProperty(PkgProps.PLATFORM_CODENAME); |
| String release = target.getProperty("ro.build.version.release"); //$NON-NLS-1$ |
| if (codename != null) { |
| return String.format("API %1$d: Android %2$s (%3$s)", |
| version.getApiLevel(), |
| release, |
| codename); |
| } |
| return String.format("API %1$d: Android %2$s", version.getApiLevel(), |
| release); |
| } |
| |
| return String.format("%1$s (API %2$s)", target.getFullName(), |
| target.getVersion().getApiString()); |
| } |
| |
| /** |
| * Sets the given tools: attribute in the given XML editor document, adding |
| * the tools name space declaration if necessary, and optionally |
| * comma-appending to an existing value. |
| * |
| * @param file the file associated with the element |
| * @param element the associated element |
| * @param description the description of the attribute (shown in the undo |
| * event) |
| * @param name the name of the attribute |
| * @param value the attribute value |
| * @param appendValue if true, add this value as a comma separated value to |
| * the existing attribute value, if any |
| */ |
| public static void setToolsAttribute( |
| @NonNull final IFile file, |
| @NonNull final Element element, |
| @NonNull final String description, |
| @NonNull final String name, |
| @Nullable final String value, |
| final boolean appendValue) { |
| IModelManager modelManager = StructuredModelManager.getModelManager(); |
| if (modelManager == null) { |
| return; |
| } |
| |
| try { |
| IStructuredModel model = null; |
| if (model == null) { |
| model = modelManager.getModelForEdit(file); |
| } |
| if (model != null) { |
| try { |
| model.aboutToChangeModel(); |
| if (model instanceof IDOMModel) { |
| IDOMModel domModel = (IDOMModel) model; |
| Document doc = domModel.getDocument(); |
| if (doc != null && element.getOwnerDocument() == doc) { |
| String prefix = XmlUtils.lookupNamespacePrefix(element, TOOLS_URI, |
| null, true); |
| if (prefix == null) { |
| // Add in new prefix... |
| prefix = XmlUtils.lookupNamespacePrefix(element, |
| TOOLS_URI, TOOLS_PREFIX, true); |
| } |
| |
| String v = value; |
| if (appendValue && v != null) { |
| String prev = element.getAttributeNS(TOOLS_URI, name); |
| if (prev.length() > 0) { |
| v = prev + ',' + value; |
| } |
| } |
| |
| // Use the non-namespace form of set attribute since we can't |
| // reference the namespace until the model has been reloaded |
| if (v != null) { |
| element.setAttribute(prefix + ':' + name, v); |
| } else { |
| element.removeAttribute(prefix + ':' + name); |
| } |
| } |
| } |
| } finally { |
| model.changedModel(); |
| String updated = model.getStructuredDocument().get(); |
| model.releaseFromEdit(); |
| model.save(file); |
| |
| // Must also force a save on disk since the above model.save(file) often |
| // (always?) has no effect. |
| ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager(); |
| NullProgressMonitor monitor = new NullProgressMonitor(); |
| IPath path = file.getFullPath(); |
| manager.connect(path, LocationKind.IFILE, monitor); |
| try { |
| ITextFileBuffer buffer = manager.getTextFileBuffer(path, |
| LocationKind.IFILE); |
| IDocument currentDocument = buffer.getDocument(); |
| currentDocument.set(updated); |
| buffer.commit(monitor, true); |
| } finally { |
| manager.disconnect(path, LocationKind.IFILE, monitor); |
| } |
| } |
| } |
| } catch (Exception e) { |
| AdtPlugin.log(e, null); |
| } |
| } |
| |
| /** |
| * Returns the Android version and code name of the given API level |
| * |
| * @param api the api level |
| * @return a suitable version display name |
| */ |
| public static String getAndroidName(int api) { |
| if (api <= SdkVersionInfo.HIGHEST_KNOWN_API) { |
| return SdkVersionInfo.getAndroidName(api); |
| } |
| |
| // Consult SDK manager to see if we know any more (later) names, |
| // installed by user |
| Sdk sdk = Sdk.getCurrent(); |
| if (sdk != null) { |
| for (IAndroidTarget target : sdk.getTargets()) { |
| if (target.isPlatform()) { |
| AndroidVersion version = target.getVersion(); |
| if (version.getApiLevel() == api) { |
| return getTargetLabel(target); |
| } |
| } |
| } |
| } |
| |
| return "API " + api; |
| } |
| |
| /** |
| * Returns the highest known API level to this version of ADT. The |
| * {@link #getAndroidName(int)} method will return real names up to and |
| * including this number. |
| * |
| * @return the highest known API number |
| */ |
| public static int getHighestKnownApiLevel() { |
| return SdkVersionInfo.HIGHEST_KNOWN_API; |
| } |
| |
| /** |
| * Returns a list of known API names |
| * |
| * @return a list of string API names, starting from 1 and up through the |
| * maximum known versions (with no gaps) |
| */ |
| public static String[] getKnownVersions() { |
| int max = getHighestKnownApiLevel(); |
| Sdk sdk = Sdk.getCurrent(); |
| if (sdk != null) { |
| for (IAndroidTarget target : sdk.getTargets()) { |
| if (target.isPlatform()) { |
| AndroidVersion version = target.getVersion(); |
| if (!version.isPreview()) { |
| max = Math.max(max, version.getApiLevel()); |
| } |
| } |
| } |
| } |
| |
| String[] versions = new String[max]; |
| for (int api = 1; api <= max; api++) { |
| versions[api-1] = getAndroidName(api); |
| } |
| |
| return versions; |
| } |
| |
| /** |
| * Returns the Android project(s) that are selected or active, if any. This |
| * considers the selection, the active editor, etc. |
| * |
| * @param selection the current selection |
| * @return a list of projects, possibly empty (but never null) |
| */ |
| @NonNull |
| public static List<IProject> getSelectedProjects(@Nullable ISelection selection) { |
| List<IProject> projects = new ArrayList<IProject>(); |
| |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| // get the unique selected item. |
| Iterator<?> iterator = structuredSelection.iterator(); |
| while (iterator.hasNext()) { |
| Object element = iterator.next(); |
| |
| // First look up the resource (since some adaptables |
| // provide an IResource but not an IProject, and we can |
| // always go from IResource to IProject) |
| IResource resource = null; |
| if (element instanceof IResource) { // may include IProject |
| resource = (IResource) element; |
| } else if (element instanceof IAdaptable) { |
| IAdaptable adaptable = (IAdaptable)element; |
| Object adapter = adaptable.getAdapter(IResource.class); |
| resource = (IResource) adapter; |
| } |
| |
| // get the project object from it. |
| IProject project = null; |
| if (resource != null) { |
| project = resource.getProject(); |
| } else if (element instanceof IAdaptable) { |
| project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); |
| } |
| |
| if (project != null && !projects.contains(project)) { |
| projects.add(project); |
| } |
| } |
| } |
| |
| if (projects.isEmpty()) { |
| // Try to look at the active editor instead |
| IFile file = AdtUtils.getActiveFile(); |
| if (file != null) { |
| projects.add(file.getProject()); |
| } |
| } |
| |
| if (projects.isEmpty()) { |
| // If we didn't find a default project based on the selection, check how many |
| // open Android projects we can find in the current workspace. If there's only |
| // one, we'll just select it by default. |
| IJavaProject[] open = AdtUtils.getOpenAndroidProjects(); |
| for (IJavaProject project : open) { |
| projects.add(project.getProject()); |
| } |
| return projects; |
| } else { |
| // Make sure all the projects are Android projects |
| List<IProject> androidProjects = new ArrayList<IProject>(projects.size()); |
| for (IProject project : projects) { |
| if (BaseProjectHelper.isAndroidProject(project)) { |
| androidProjects.add(project); |
| } |
| } |
| return androidProjects; |
| } |
| } |
| |
| private static Boolean sEclipse4; |
| |
| /** |
| * Returns true if the running Eclipse is version 4.x or later |
| * |
| * @return true if the current Eclipse version is 4.x or later, false |
| * otherwise |
| */ |
| public static boolean isEclipse4() { |
| if (sEclipse4 == null) { |
| sEclipse4 = Platform.getBundle("org.eclipse.e4.ui.model.workbench") != null; //$NON-NLS-1$ |
| } |
| |
| return sEclipse4; |
| } |
| |
| /** |
| * Reads the contents of an {@link IFile} and return it as a byte array |
| * |
| * @param file the file to be read |
| * @return the String read from the file, or null if there was an error |
| */ |
| @SuppressWarnings("resource") // Eclipse doesn't understand Closeables.closeQuietly yet |
| @Nullable |
| public static byte[] readData(@NonNull IFile file) { |
| InputStream contents = null; |
| try { |
| contents = file.getContents(); |
| return ByteStreams.toByteArray(contents); |
| } catch (Exception e) { |
| // Pass -- just return null |
| } finally { |
| Closeables.closeQuietly(contents); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Ensure that a given folder (and all its parents) are created. This implements |
| * the equivalent of {@link File#mkdirs()} for {@link IContainer} folders. |
| * |
| * @param container the container to ensure exists |
| * @throws CoreException if an error occurs |
| */ |
| public static void ensureExists(@Nullable IContainer container) throws CoreException { |
| if (container == null || container.exists()) { |
| return; |
| } |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IFolder folder = root.getFolder(container.getFullPath()); |
| ensureExists(folder); |
| } |
| |
| private static void ensureExists(IFolder folder) throws CoreException { |
| if (folder != null && !folder.exists()) { |
| IContainer parent = folder.getParent(); |
| if (parent instanceof IFolder) { |
| ensureExists((IFolder) parent); |
| } |
| folder.create(false, false, null); |
| } |
| } |
| |
| /** |
| * Format the given floating value into an XML string, omitting decimals if |
| * 0 |
| * |
| * @param value the value to be formatted |
| * @return the corresponding XML string for the value |
| */ |
| public static String formatFloatAttribute(float value) { |
| if (value != (int) value) { |
| // Run String.format without a locale, because we don't want locale-specific |
| // conversions here like separating the decimal part with a comma instead of a dot! |
| return String.format((Locale) null, "%.2f", value); //$NON-NLS-1$ |
| } else { |
| return Integer.toString((int) value); |
| } |
| } |
| |
| /** |
| * Creates all the directories required for the given path. |
| * |
| * @param wsPath the path to create all the parent directories for |
| * @return true if all the parent directories were created |
| */ |
| public static boolean createWsParentDirectory(IContainer wsPath) { |
| if (wsPath.getType() == IResource.FOLDER) { |
| if (wsPath.exists()) { |
| return true; |
| } |
| |
| IFolder folder = (IFolder) wsPath; |
| try { |
| if (createWsParentDirectory(wsPath.getParent())) { |
| folder.create(true /* force */, true /* local */, null /* monitor */); |
| return true; |
| } |
| } catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Lists the files of the given directory and returns them as an array which |
| * is never null. This simplifies processing file listings from for each |
| * loops since {@link File#listFiles} can return null. This method simply |
| * wraps it and makes sure it returns an empty array instead if necessary. |
| * |
| * @param dir the directory to list |
| * @return the children, or empty if it has no children, is not a directory, |
| * etc. |
| */ |
| @NonNull |
| public static File[] listFiles(File dir) { |
| File[] files = dir.listFiles(); |
| if (files != null) { |
| return files; |
| } else { |
| return new File[0]; |
| } |
| } |
| |
| /** |
| * Closes all open editors that are showing a file for the given project. This method |
| * should be called when a project is closed or deleted. |
| * <p> |
| * This method can be called from any thread, but if it is not called on the GUI thread |
| * the editor will be closed asynchronously. |
| * |
| * @param project the project to close all editors for |
| * @param save whether unsaved editors should be saved first |
| */ |
| public static void closeEditors(@NonNull final IProject project, final boolean save) { |
| final Display display = AdtPlugin.getDisplay(); |
| if (display == null || display.isDisposed()) { |
| return; |
| } |
| if (display.getThread() != Thread.currentThread()) { |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| closeEditors(project, save); |
| } |
| }); |
| return; |
| } |
| |
| // Close editors for removed files |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| for (IWorkbenchWindow window : workbench.getWorkbenchWindows()) { |
| for (IWorkbenchPage page : window.getPages()) { |
| List<IEditorReference> matching = null; |
| for (IEditorReference ref : page.getEditorReferences()) { |
| boolean close = false; |
| try { |
| IEditorInput input = ref.getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| IFileEditorInput fileInput = (IFileEditorInput) input; |
| if (project.equals(fileInput.getFile().getProject())) { |
| close = true; |
| } |
| } |
| } catch (PartInitException ex) { |
| close = true; |
| } |
| if (close) { |
| if (matching == null) { |
| matching = new ArrayList<IEditorReference>(2); |
| } |
| matching.add(ref); |
| } |
| } |
| if (matching != null) { |
| IEditorReference[] refs = new IEditorReference[matching.size()]; |
| page.closeEditors(matching.toArray(refs), save); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Closes all open editors for the given file. Note that a file can be open in |
| * more than one editor, for example by using Open With on the file to choose different |
| * editors. |
| * <p> |
| * This method can be called from any thread, but if it is not called on the GUI thread |
| * the editor will be closed asynchronously. |
| * |
| * @param file the file whose editors should be closed. |
| * @param save whether unsaved editors should be saved first |
| */ |
| public static void closeEditors(@NonNull final IFile file, final boolean save) { |
| final Display display = AdtPlugin.getDisplay(); |
| if (display == null || display.isDisposed()) { |
| return; |
| } |
| if (display.getThread() != Thread.currentThread()) { |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| closeEditors(file, save); |
| } |
| }); |
| return; |
| } |
| |
| // Close editors for removed files |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| for (IWorkbenchWindow window : workbench.getWorkbenchWindows()) { |
| for (IWorkbenchPage page : window.getPages()) { |
| List<IEditorReference> matching = null; |
| for (IEditorReference ref : page.getEditorReferences()) { |
| boolean close = false; |
| try { |
| IEditorInput input = ref.getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| IFileEditorInput fileInput = (IFileEditorInput) input; |
| if (file.equals(fileInput.getFile())) { |
| close = true; |
| } |
| } |
| } catch (PartInitException ex) { |
| close = true; |
| } |
| if (close) { |
| // Found |
| if (matching == null) { |
| matching = new ArrayList<IEditorReference>(2); |
| } |
| matching.add(ref); |
| // We don't break here in case the file is |
| // opened multiple times with different editors. |
| } |
| } |
| if (matching != null) { |
| IEditorReference[] refs = new IEditorReference[matching.size()]; |
| page.closeEditors(matching.toArray(refs), save); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the offset region of the given 0-based line number in the given |
| * file |
| * |
| * @param file the file to look up the line number in |
| * @param line the line number (0-based, meaning that the first line is line |
| * 0) |
| * @return the corresponding offset range, or null |
| */ |
| @Nullable |
| public static IRegion getRegionOfLine(@NonNull IFile file, int line) { |
| IDocumentProvider provider = new TextFileDocumentProvider(); |
| try { |
| provider.connect(file); |
| IDocument document = provider.getDocument(file); |
| if (document != null) { |
| return document.getLineInformation(line); |
| } |
| } catch (Exception e) { |
| AdtPlugin.log(e, "Can't find range information for %1$s", file.getName()); |
| } finally { |
| provider.disconnect(file); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns all resource variations for the given file |
| * |
| * @param file resource file, which should be an XML file in one of the |
| * various resource folders, e.g. res/layout, res/values-xlarge, etc. |
| * @param includeSelf if true, include the file itself in the list, |
| * otherwise exclude it |
| * @return a list of all the resource variations |
| */ |
| public static List<IFile> getResourceVariations(@Nullable IFile file, boolean includeSelf) { |
| if (file == null) { |
| return Collections.emptyList(); |
| } |
| |
| // Compute the set of layout files defining this layout resource |
| List<IFile> variations = new ArrayList<IFile>(); |
| String name = file.getName(); |
| IContainer parent = file.getParent(); |
| if (parent != null) { |
| IContainer resFolder = parent.getParent(); |
| if (resFolder != null) { |
| String parentName = parent.getName(); |
| String prefix = parentName; |
| int qualifiers = prefix.indexOf('-'); |
| |
| if (qualifiers != -1) { |
| parentName = prefix.substring(0, qualifiers); |
| prefix = prefix.substring(0, qualifiers + 1); |
| } else { |
| prefix = prefix + '-'; |
| } |
| try { |
| for (IResource resource : resFolder.members()) { |
| String n = resource.getName(); |
| if ((n.startsWith(prefix) || n.equals(parentName)) |
| && resource instanceof IContainer) { |
| IContainer layoutFolder = (IContainer) resource; |
| IResource r = layoutFolder.findMember(name); |
| if (r instanceof IFile) { |
| IFile variation = (IFile) r; |
| if (!includeSelf && file.equals(variation)) { |
| continue; |
| } |
| variations.add(variation); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| AdtPlugin.log(e, null); |
| } |
| } |
| } |
| |
| return variations; |
| } |
| |
| /** |
| * Returns whether the current thread is the UI thread |
| * |
| * @return true if the current thread is the UI thread |
| */ |
| public static boolean isUiThread() { |
| return AdtPlugin.getDisplay() != null |
| && AdtPlugin.getDisplay().getThread() == Thread.currentThread(); |
| } |
| |
| /** |
| * Replaces any {@code \\uNNNN} references in the given string with the corresponding |
| * unicode characters. |
| * |
| * @param s the string to perform replacements in |
| * @return the string with unicode escapes replaced with actual characters |
| */ |
| @NonNull |
| public static String replaceUnicodeEscapes(@NonNull String s) { |
| // Handle unicode escapes |
| if (s.indexOf("\\u") != -1) { //$NON-NLS-1$ |
| StringBuilder sb = new StringBuilder(s.length()); |
| for (int i = 0, n = s.length(); i < n; i++) { |
| char c = s.charAt(i); |
| if (c == '\\' && i < n - 1) { |
| char next = s.charAt(i + 1); |
| if (next == 'u' && i < n - 5) { // case sensitive |
| String hex = s.substring(i + 2, i + 6); |
| try { |
| int unicodeValue = Integer.parseInt(hex, 16); |
| sb.append((char) unicodeValue); |
| i += 5; |
| continue; |
| } catch (NumberFormatException nufe) { |
| // Invalid escape: Just proceed to literally transcribe it |
| sb.append(c); |
| } |
| } else { |
| sb.append(c); |
| sb.append(next); |
| i++; |
| continue; |
| } |
| } else { |
| sb.append(c); |
| } |
| } |
| s = sb.toString(); |
| } |
| |
| return s; |
| } |
| |
| /** |
| * Looks up the {@link ResourceFolderType} corresponding to a given |
| * {@link ResourceType}: the folder where those resources can be found. |
| * <p> |
| * Note that {@link ResourceType#ID} is a special case: it can not just |
| * be defined in {@link ResourceFolderType#VALUES}, but it can also be |
| * defined inline via {@code @+id} in {@link ResourceFolderType#LAYOUT} and |
| * {@link ResourceFolderType#MENU} folders. |
| * |
| * @param type the resource type |
| * @return the corresponding resource folder type |
| */ |
| @NonNull |
| public static ResourceFolderType getFolderTypeFor(@NonNull ResourceType type) { |
| switch (type) { |
| case ANIM: |
| return ResourceFolderType.ANIM; |
| case ANIMATOR: |
| return ResourceFolderType.ANIMATOR; |
| case ARRAY: |
| return ResourceFolderType.VALUES; |
| case COLOR: |
| return ResourceFolderType.COLOR; |
| case DRAWABLE: |
| return ResourceFolderType.DRAWABLE; |
| case INTERPOLATOR: |
| return ResourceFolderType.INTERPOLATOR; |
| case LAYOUT: |
| return ResourceFolderType.LAYOUT; |
| case MENU: |
| return ResourceFolderType.MENU; |
| case MIPMAP: |
| return ResourceFolderType.MIPMAP; |
| case RAW: |
| return ResourceFolderType.RAW; |
| case XML: |
| return ResourceFolderType.XML; |
| case ATTR: |
| case BOOL: |
| case DECLARE_STYLEABLE: |
| case DIMEN: |
| case FRACTION: |
| case ID: |
| case INTEGER: |
| case PLURALS: |
| case PUBLIC: |
| case STRING: |
| case STYLE: |
| case STYLEABLE: |
| return ResourceFolderType.VALUES; |
| default: |
| assert false : type; |
| return ResourceFolderType.VALUES; |
| |
| } |
| } |
| |
| /** |
| * Looks up the {@link ResourceType} defined in a given {@link ResourceFolderType}. |
| * <p> |
| * Note that for {@link ResourceFolderType#VALUES} there are many, many |
| * different types of resources that can be defined, so this method returns |
| * {@code null} for that scenario. |
| * <p> |
| * Note also that {@link ResourceType#ID} is a special case: it can not just |
| * be defined in {@link ResourceFolderType#VALUES}, but it can also be |
| * defined inline via {@code @+id} in {@link ResourceFolderType#LAYOUT} and |
| * {@link ResourceFolderType#MENU} folders. |
| * |
| * @param folderType the resource folder type |
| * @return the corresponding resource type, or null if {@code folderType} is |
| * {@link ResourceFolderType#VALUES} |
| */ |
| @Nullable |
| public static ResourceType getResourceTypeFor(@NonNull ResourceFolderType folderType) { |
| switch (folderType) { |
| case ANIM: |
| return ResourceType.ANIM; |
| case ANIMATOR: |
| return ResourceType.ANIMATOR; |
| case COLOR: |
| return ResourceType.COLOR; |
| case DRAWABLE: |
| return ResourceType.DRAWABLE; |
| case INTERPOLATOR: |
| return ResourceType.INTERPOLATOR; |
| case LAYOUT: |
| return ResourceType.LAYOUT; |
| case MENU: |
| return ResourceType.MENU; |
| case MIPMAP: |
| return ResourceType.MIPMAP; |
| case RAW: |
| return ResourceType.RAW; |
| case XML: |
| return ResourceType.XML; |
| case VALUES: |
| return null; |
| default: |
| assert false : folderType; |
| return null; |
| } |
| } |
| } |