aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java527
1 files changed, 527 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
new file mode 100644
index 000000000..57632ea87
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2007 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.internal.project;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.google.common.collect.Lists;
+
+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.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jdt.ui.actions.OpenJavaPerspectiveAction;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods to manipulate projects.
+ */
+public final class BaseProjectHelper {
+
+ public static final String TEST_CLASS_OK = null;
+
+ /**
+ * Project filter to be used with {@link BaseProjectHelper#getAndroidProjects(IProjectFilter)}.
+ */
+ public static interface IProjectFilter {
+ boolean accept(IProject project);
+ }
+
+ /**
+ * returns a list of source classpath for a specified project
+ * @param javaProject
+ * @return a list of path relative to the workspace root.
+ */
+ @NonNull
+ public static List<IPath> getSourceClasspaths(IJavaProject javaProject) {
+ List<IPath> sourceList = Lists.newArrayList();
+ IClasspathEntry[] classpaths = javaProject.readRawClasspath();
+ if (classpaths != null) {
+ for (IClasspathEntry e : classpaths) {
+ if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+ sourceList.add(e.getPath());
+ }
+ }
+ }
+
+ return sourceList;
+ }
+
+ /**
+ * returns a list of source classpath for a specified project
+ * @param project
+ * @return a list of path relative to the workspace root.
+ */
+ public static List<IPath> getSourceClasspaths(IProject project) {
+ IJavaProject javaProject = JavaCore.create(project);
+ return getSourceClasspaths(javaProject);
+ }
+
+ /**
+ * Adds a marker to a file on a specific line. This methods catches thrown
+ * {@link CoreException}, and returns null instead.
+ * @param resource the resource to be marked
+ * @param markerId The id of the marker to add.
+ * @param message the message associated with the mark
+ * @param lineNumber the line number where to put the mark. If line is < 1, it puts the marker
+ * on line 1,
+ * @param severity the severity of the marker.
+ * @return the IMarker that was added or null if it failed to add one.
+ */
+ public final static IMarker markResource(IResource resource, String markerId,
+ String message, int lineNumber, int severity) {
+ return markResource(resource, markerId, message, lineNumber, -1, -1, severity);
+ }
+
+ /**
+ * Adds a marker to a file on a specific line, for a specific range of text. This
+ * methods catches thrown {@link CoreException}, and returns null instead.
+ *
+ * @param resource the resource to be marked
+ * @param markerId The id of the marker to add.
+ * @param message the message associated with the mark
+ * @param lineNumber the line number where to put the mark. If line is < 1, it puts
+ * the marker on line 1,
+ * @param startOffset the beginning offset of the marker (relative to the beginning of
+ * the document, not the line), or -1 for no range
+ * @param endOffset the ending offset of the marker
+ * @param severity the severity of the marker.
+ * @return the IMarker that was added or null if it failed to add one.
+ */
+ @Nullable
+ public final static IMarker markResource(IResource resource, String markerId,
+ String message, int lineNumber, int startOffset, int endOffset, int severity) {
+ if (!resource.isAccessible()) {
+ return null;
+ }
+
+ try {
+ IMarker marker = resource.createMarker(markerId);
+ marker.setAttribute(IMarker.MESSAGE, message);
+ marker.setAttribute(IMarker.SEVERITY, severity);
+
+ // if marker is text type, enforce a line number so that it shows in the editor
+ // somewhere (line 1)
+ if (lineNumber < 1 && marker.isSubtypeOf(IMarker.TEXT)) {
+ lineNumber = 1;
+ }
+
+ if (lineNumber >= 1) {
+ marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
+ }
+
+ if (startOffset != -1) {
+ marker.setAttribute(IMarker.CHAR_START, startOffset);
+ marker.setAttribute(IMarker.CHAR_END, endOffset);
+ }
+
+ // on Windows, when adding a marker to a project, it takes a refresh for the marker
+ // to show. In order to fix this we're forcing a refresh of elements receiving
+ // markers (and only the element, not its children), to force the marker display.
+ resource.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
+
+ return marker;
+ } catch (CoreException e) {
+ AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", //$NON-NLS-1$
+ markerId, resource.getFullPath());
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a marker to a resource. This methods catches thrown {@link CoreException},
+ * and returns null instead.
+ * @param resource the file to be marked
+ * @param markerId The id of the marker to add.
+ * @param message the message associated with the mark
+ * @param severity the severity of the marker.
+ * @return the IMarker that was added or null if it failed to add one.
+ */
+ @Nullable
+ public final static IMarker markResource(IResource resource, String markerId,
+ String message, int severity) {
+ return markResource(resource, markerId, message, -1, severity);
+ }
+
+ /**
+ * Adds a marker to an {@link IProject}. This method does not catch {@link CoreException}, like
+ * {@link #markResource(IResource, String, String, int)}.
+ *
+ * @param project the project to be marked
+ * @param markerId The id of the marker to add.
+ * @param message the message associated with the mark
+ * @param severity the severity of the marker.
+ * @param priority the priority of the marker
+ * @return the IMarker that was added.
+ * @throws CoreException if the marker cannot be added
+ */
+ @Nullable
+ public final static IMarker markProject(IProject project, String markerId,
+ String message, int severity, int priority) throws CoreException {
+ if (!project.isAccessible()) {
+ return null;
+ }
+
+ IMarker marker = project.createMarker(markerId);
+ marker.setAttribute(IMarker.MESSAGE, message);
+ marker.setAttribute(IMarker.SEVERITY, severity);
+ marker.setAttribute(IMarker.PRIORITY, priority);
+
+ // on Windows, when adding a marker to a project, it takes a refresh for the marker
+ // to show. In order to fix this we're forcing a refresh of elements receiving
+ // markers (and only the element, not its children), to force the marker display.
+ project.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
+
+ return marker;
+ }
+
+ /**
+ * Tests that a class name is valid for usage in the manifest.
+ * <p/>
+ * This tests the class existence, that it can be instantiated (ie it must not be abstract,
+ * nor non static if enclosed), and that it extends the proper super class (not necessarily
+ * directly)
+ * @param javaProject the {@link IJavaProject} containing the class.
+ * @param className the fully qualified name of the class to test.
+ * @param superClassName the fully qualified name of the expected super class.
+ * @param testVisibility if <code>true</code>, the method will check the visibility of the class
+ * or of its constructors.
+ * @return {@link #TEST_CLASS_OK} or an error message.
+ */
+ public final static String testClassForManifest(IJavaProject javaProject, String className,
+ String superClassName, boolean testVisibility) {
+ try {
+ // replace $ by .
+ String javaClassName = className.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // look for the IType object for this class
+ IType type = javaProject.findType(javaClassName);
+ if (type != null && type.exists()) {
+ // test that the class is not abstract
+ int flags = type.getFlags();
+ if (Flags.isAbstract(flags)) {
+ return String.format("%1$s is abstract", className);
+ }
+
+ // test whether the class is public or not.
+ if (testVisibility && Flags.isPublic(flags) == false) {
+ // if its not public, it may have a public default constructor,
+ // which would then be fine.
+ IMethod basicConstructor = type.getMethod(type.getElementName(), new String[0]);
+ if (basicConstructor != null && basicConstructor.exists()) {
+ int constructFlags = basicConstructor.getFlags();
+ if (Flags.isPublic(constructFlags) == false) {
+ return String.format(
+ "%1$s or its default constructor must be public for the system to be able to instantiate it",
+ className);
+ }
+ } else {
+ return String.format(
+ "%1$s must be public, or the system will not be able to instantiate it.",
+ className);
+ }
+ }
+
+ // If it's enclosed, test that it's static. If its declaring class is enclosed
+ // as well, test that it is also static, and public.
+ IType declaringType = type;
+ do {
+ IType tmpType = declaringType.getDeclaringType();
+ if (tmpType != null) {
+ if (tmpType.exists()) {
+ flags = declaringType.getFlags();
+ if (Flags.isStatic(flags) == false) {
+ return String.format("%1$s is enclosed, but not static",
+ declaringType.getFullyQualifiedName());
+ }
+
+ flags = tmpType.getFlags();
+ if (testVisibility && Flags.isPublic(flags) == false) {
+ return String.format("%1$s is not public",
+ tmpType.getFullyQualifiedName());
+ }
+ } else {
+ // if it doesn't exist, we need to exit so we may as well mark it null.
+ tmpType = null;
+ }
+ }
+ declaringType = tmpType;
+ } while (declaringType != null);
+
+ // test the class inherit from the specified super class.
+ // get the type hierarchy
+ ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
+
+ // if the super class is not the reference class, it may inherit from
+ // it so we get its supertype. At some point it will be null and we
+ // will stop
+ IType superType = type;
+ boolean foundProperSuperClass = false;
+ while ((superType = hierarchy.getSuperclass(superType)) != null &&
+ superType.exists()) {
+ if (superClassName.equals(superType.getFullyQualifiedName())) {
+ foundProperSuperClass = true;
+ }
+ }
+
+ // didn't find the proper superclass? return false.
+ if (foundProperSuperClass == false) {
+ return String.format("%1$s does not extend %2$s", className, superClassName);
+ }
+
+ return TEST_CLASS_OK;
+ } else {
+ return String.format("Class %1$s does not exist", className);
+ }
+ } catch (JavaModelException e) {
+ return String.format("%1$s: %2$s", className, e.getMessage());
+ }
+ }
+
+ /**
+ * Returns the {@link IJavaProject} for a {@link IProject} object.
+ * <p/>
+ * This checks if the project has the Java Nature first.
+ * @param project
+ * @return the IJavaProject or null if the project couldn't be created or if the project
+ * does not have the Java Nature.
+ * @throws CoreException if this method fails. Reasons include:
+ * <ul><li>This project does not exist.</li><li>This project is not open.</li></ul>
+ */
+ public static IJavaProject getJavaProject(IProject project) throws CoreException {
+ if (project != null && project.hasNature(JavaCore.NATURE_ID)) {
+ return JavaCore.create(project);
+ }
+ return null;
+ }
+
+ /**
+ * Reveals a specific line in the source file defining a specified class,
+ * for a specific project.
+ * @param project
+ * @param className
+ * @param line
+ * @return true if the source was revealed
+ */
+ public static boolean revealSource(IProject project, String className, int line) {
+ // Inner classes are pointless: All we need is the enclosing type to find the file, and the
+ // line number.
+ // Since the anonymous ones will cause IJavaProject#findType to fail, we remove
+ // all of them.
+ int pos = className.indexOf('$');
+ if (pos != -1) {
+ className = className.substring(0, pos);
+ }
+
+ // get the java project
+ IJavaProject javaProject = JavaCore.create(project);
+
+ try {
+ // look for the IType matching the class name.
+ IType result = javaProject.findType(className);
+ if (result != null && result.exists()) {
+ // before we show the type in an editor window, we make sure the current
+ // workbench page has an editor area (typically the ddms perspective doesn't).
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ if (page.isEditorAreaVisible() == false) {
+ // no editor area? we open the java perspective.
+ new OpenJavaPerspectiveAction().run();
+ }
+
+ IEditorPart editor = JavaUI.openInEditor(result);
+ if (editor instanceof ITextEditor) {
+ // get the text editor that was just opened.
+ ITextEditor textEditor = (ITextEditor)editor;
+
+ IEditorInput input = textEditor.getEditorInput();
+
+ // get the location of the line to show.
+ IDocumentProvider documentProvider = textEditor.getDocumentProvider();
+ IDocument document = documentProvider.getDocument(input);
+ IRegion lineInfo = document.getLineInformation(line - 1);
+
+ // select and reveal the line.
+ textEditor.selectAndReveal(lineInfo.getOffset(), lineInfo.getLength());
+ }
+
+ return true;
+ }
+ } catch (JavaModelException e) {
+ } catch (PartInitException e) {
+ } catch (BadLocationException e) {
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the list of android-flagged projects. This list contains projects that are opened
+ * in the workspace and that are flagged as android project (through the android nature)
+ * @param filter an optional filter to control which android project are returned. Can be null.
+ * @return an array of IJavaProject, which can be empty if no projects match.
+ */
+ public static @NonNull IJavaProject[] getAndroidProjects(@Nullable IProjectFilter filter) {
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ IJavaModel javaModel = JavaCore.create(workspaceRoot);
+
+ return getAndroidProjects(javaModel, filter);
+ }
+
+ /**
+ * Returns the list of android-flagged projects for the specified java Model.
+ * This list contains projects that are opened in the workspace and that are flagged as android
+ * project (through the android nature)
+ * @param javaModel the Java Model object corresponding for the current workspace root.
+ * @param filter an optional filter to control which android project are returned. Can be null.
+ * @return an array of IJavaProject, which can be empty if no projects match.
+ */
+ @NonNull
+ public static IJavaProject[] getAndroidProjects(@NonNull IJavaModel javaModel,
+ @Nullable IProjectFilter filter) {
+ // get the java projects
+ IJavaProject[] javaProjectList = null;
+ try {
+ javaProjectList = javaModel.getJavaProjects();
+ }
+ catch (JavaModelException jme) {
+ return new IJavaProject[0];
+ }
+
+ // temp list to build the android project array
+ ArrayList<IJavaProject> androidProjectList = new ArrayList<IJavaProject>();
+
+ // loop through the projects and add the android flagged projects to the temp list.
+ for (IJavaProject javaProject : javaProjectList) {
+ // get the workspace project object
+ IProject project = javaProject.getProject();
+
+ // check if it's an android project based on its nature
+ if (isAndroidProject(project)) {
+ if (filter == null || filter.accept(project)) {
+ androidProjectList.add(javaProject);
+ }
+ }
+ }
+
+ // return the android projects list.
+ return androidProjectList.toArray(new IJavaProject[androidProjectList.size()]);
+ }
+
+ /**
+ * Returns true if the given project is an Android project (e.g. is a Java project
+ * that also has the Android nature)
+ *
+ * @param project the project to test
+ * @return true if the given project is an Android project
+ */
+ public static boolean isAndroidProject(IProject project) {
+ // check if it's an android project based on its nature
+ try {
+ return project.hasNature(AdtConstants.NATURE_DEFAULT);
+ } catch (CoreException e) {
+ // this exception, thrown by IProject.hasNature(), means the project either doesn't
+ // exist or isn't opened. So, in any case we just skip it (the exception will
+ // bypass the ArrayList.add()
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the {@link IFolder} representing the output for the project for Android specific
+ * files.
+ * <p>
+ * The project must be a java project and be opened, or the method will return null.
+ * @param project the {@link IProject}
+ * @return an IFolder item or null.
+ */
+ public final static IFolder getJavaOutputFolder(IProject project) {
+ try {
+ if (project.isOpen() && project.hasNature(JavaCore.NATURE_ID)) {
+ // get a java project from the normal project object
+ IJavaProject javaProject = JavaCore.create(project);
+
+ IPath path = javaProject.getOutputLocation();
+ path = path.removeFirstSegments(1);
+ return project.getFolder(path);
+ }
+ } catch (JavaModelException e) {
+ // Let's do nothing and return null
+ } catch (CoreException e) {
+ // Let's do nothing and return null
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link IFolder} representing the output for the project for compiled Java
+ * files.
+ * <p>
+ * The project must be a java project and be opened, or the method will return null.
+ * @param project the {@link IProject}
+ * @return an IFolder item or null.
+ */
+ @Nullable
+ public final static IFolder getAndroidOutputFolder(IProject project) {
+ try {
+ if (project.isOpen() && project.hasNature(JavaCore.NATURE_ID)) {
+ return project.getFolder(SdkConstants.FD_OUTPUT);
+ }
+ } catch (JavaModelException e) {
+ // Let's do nothing and return null
+ } catch (CoreException e) {
+ // Let's do nothing and return null
+ }
+ return null;
+ }
+
+}