Java tutorial
/******************************************************************************* * Copyright (c) 2011, 2012 JavaTime project and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * JavaTime project, Eirik Gronsund - initial implementation *******************************************************************************/ package no.javatime.inplace.bundleproject; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import no.javatime.inplace.InPlace; import no.javatime.inplace.builder.JavaTimeNature; import no.javatime.inplace.bundlemanager.InPlaceException; import no.javatime.inplace.bundlemanager.ProjectLocationException; import no.javatime.inplace.dependencies.CircularReferenceException; import no.javatime.inplace.dependencies.ProjectSorter; import no.javatime.inplace.statushandler.BundleStatus; import no.javatime.inplace.statushandler.IBundleStatus.StatusCode; import no.javatime.util.messages.Category; import no.javatime.util.messages.ExceptionMessage; import no.javatime.util.messages.Message; import no.javatime.util.messages.TraceMessage; import no.javatime.util.messages.WarnMessage; import org.eclipse.core.resources.IFile; 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.IWorkspaceDescription; 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.Path; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.pde.core.project.IBundleProjectDescription; import org.eclipse.pde.core.project.IRequiredBundleDescription; import org.eclipse.ui.statushandlers.StatusManager; import org.osgi.framework.Bundle; /** * Utility to: * <ol> * <li>Get activation status of the workspace * <li>Retrieve workspace projects and differentiate them by nature. * <li>Get bundle location from projects. * <li>Switch between general and java projects * <li>Check build state of projects and state of the build automatic switch * <li>Get selected project in package explorer, project explorer and bundle list page * </ol> * <p> * The workspace is activated if one or more projects have the JavaTime nature. */ public class ProjectProperties { final public static String PLUGIN_NATURE_ID = "org.eclipse.pde.PluginNature"; final public static String PACKAGE_EXPLORER_ID = org.eclipse.jdt.ui.JavaUI.ID_PACKAGES; final public static String PROJECT_EXPLORER_ID = "org.eclipse.ui.navigator.ProjectExplorer"; final public static String bundleReferenceLocationScheme = Message.getInstance() .formatString("bundle_identifier_reference_scheme"); final public static String bundleFileLocationScheme = Message.getInstance() .formatString("bundle_identifier_file_scheme"); final public static String MANIFEST_FILE_NAME = Message.getInstance().formatString("manifest_file_name"); /** * Check if a project is JavaTime nature enabled (activated). The condition is satisfied if one workspace * project is activated. This only implies that one or more projects are JavaTime enabled, and does not * necessary mean that the corresponding bundle is activated. * * @return true if at least one project is activated or false if no projects are activated */ public static Boolean isProjectWorkspaceActivated() { for (IProject project : getProjects()) { if (isProjectActivated(project)) { return true; } } return false; } /** * Get all open and closed projects in workspace * * @return a list of all projects in workspace */ public static Collection<IProject> getProjects() { return Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()); } /** * Filters out all projects in the workspace registered with the JavaTime nature * * @return a list of projects with the JavaTime nature or an empty collection */ public static Collection<IProject> getActivatedProjects() { Collection<IProject> projects = new LinkedHashSet<IProject>(); for (IProject project : getProjects()) { if (isProjectActivated(project)) { projects.add(project); } } return projects; } /** * Find all projects that fulfill the requirements to be activated. Already activated projects and projects * that contributes to the UI when UI contributors are not allowed are not part of the returned collection * * @return all projects that may be activated or an empty collection * @see #isCandidateProject(IProject) */ public static Collection<IProject> getCandidateProjects() { Collection<IProject> projects = new LinkedHashSet<IProject>(); for (IProject project : getProjects()) { try { if (project.isNatureEnabled(JavaCore.NATURE_ID) && project.isNatureEnabled(PLUGIN_NATURE_ID) && !isProjectActivated(project)) { projects.add(project); } } catch (CoreException e) { // Ignore closed or non-existing project } try { if (!InPlace.getDefault().getOptionsService().isAllowUIContributions()) { projects.removeAll(getUIContributors()); } } catch (CircularReferenceException e) { // Ignore. Cycles are detected in any bundle job } catch (InPlaceException e) { StatusManager.getManager().handle( new BundleStatus(StatusCode.EXCEPTION, InPlace.PLUGIN_ID, e.getMessage(), e), StatusManager.LOG); // assume allow ui contributers } } return projects; } /** * Return all plug-in projects that have Java and plug-in nature enabled and not contributes to the UI if UI * contributors are not allowed * * @return all plug-in projects with Java, plug-in nature and not contributing to the UI, when not allowed * or an empty collection */ public static Collection<IProject> getInstallableProjects() { Collection<IProject> projects = new LinkedHashSet<IProject>(); for (IProject project : getProjects()) { try { if (project.isNatureEnabled(JavaCore.NATURE_ID) && project.isNatureEnabled(PLUGIN_NATURE_ID)) { projects.add(project); } } catch (CoreException e) { // Ignore closed or non-existing project } try { if (!InPlace.getDefault().getOptionsService().isAllowUIContributions()) { projects.removeAll(getUIContributors()); } } catch (CircularReferenceException e) { // Ignore. Cycles are detected in any bundle job } catch (InPlaceException e) { StatusManager.getManager().handle( new BundleStatus(StatusCode.EXCEPTION, InPlace.PLUGIN_ID, e.getMessage(), e), StatusManager.LOG); // assume allow ui contributers } } return projects; } /** * Return all plug-in projects that have the Java and plug-in nature enabled. * Closed and non-existing projects are discarded * * @return all plug-in projects with Java and plug-in nature or an empty collection */ public static Collection<IProject> getPlugInProjects() { Collection<IProject> projects = new LinkedHashSet<IProject>(); for (IProject project : getProjects()) { try { if (project.hasNature(JavaCore.NATURE_ID) && project.isNatureEnabled(PLUGIN_NATURE_ID)) { projects.add(project); } } catch (CoreException e) { // Ignore closed or non-existing project } } return projects; } /** * When a project has JavaTime nature enabled the project is activated. * * @param project to check for JavaTime nature * @return true if JavaTime nature is enabled for the project and false if not * @see no.javatime.inplace.bundlemanager.BundleWorkspaceImpl#isActivated(IProject) * @see no.javatime.inplace.bundlemanager.BundleWorkspaceImpl#isActivated(Long) * @see no.javatime.inplace.bundlemanager.BundleWorkspaceImpl#isActivated(Bundle) */ public static Boolean isProjectActivated(IProject project) { try { if (null != project && project.isNatureEnabled(JavaTimeNature.JAVATIME_NATURE_ID)) { return true; } } catch (CoreException e) { // Ignore closed or non-existing project } return false; } /** * Checks if this project has the Java and plug-in nature enabled. * * @return true if this is a Java plug-in project or false */ public static Boolean isPlugInProject(IProject project) { try { if (project.hasNature(JavaCore.NATURE_ID) && project.isNatureEnabled(PLUGIN_NATURE_ID)) { return true; } } catch (CoreException e) { // Ignore closed or non-existing project } return false; } /** * Checks if this project has the Java and plug-in or the JavaTime nature enabled. Projects in the UI name space are * included if UI contributed projects are allowed. * * @return true if this is a Java plug-in or JavaTime project or false */ public static Boolean isInstallableProject(IProject project) { if (isCandidateProject(project) || isProjectActivated(project)) { return true; } return false; } /** * A project is a candidate project if it is not activated, has the plug-in and Java nature enabled, and not * contributes to the UI if UI contributors are not allowed * * @param project to check for candidate nature attributes * @return true if project is not activated, a plug-in project with Java, plug-in nature and not * contributing to the UI, when not allowed. Otherwise false. */ public static Boolean isCandidateProject(IProject project) { try { if (project.hasNature(JavaCore.NATURE_ID) && project.isNatureEnabled(PLUGIN_NATURE_ID) && !isProjectActivated(project)) { if (InPlace.getDefault().getOptionsService().isAllowUIContributions()) { return true; } else { Collection<IProject> uiContributors = getUIContributors(); if (uiContributors.contains(project)) { return false; } else { return true; } } } } catch (CoreException e) { // Ignore closed or non-existing project } catch (CircularReferenceException e) { // Ignore. Cycles are detected in any bundle job } catch (InPlaceException e) { StatusManager.getManager().handle( new BundleStatus(StatusCode.EXCEPTION, InPlace.PLUGIN_ID, e.getMessage(), e), StatusManager.LOG); // assume not candidate } return false; } /** * Get all plug-in projects that contributes to the UI and their requiring projects * * @return set of plug-in projects contributing to the UI or an empty collection * @throws CircularReferenceException if cycles are detected in the project graph */ public static Collection<IProject> getUIContributors() throws CircularReferenceException { Collection<IProject> projects = new LinkedHashSet<IProject>(); for (IProject project : getProjects()) { try { if (project.hasNature(JavaCore.NATURE_ID) && project.isNatureEnabled(PLUGIN_NATURE_ID) && contributesToTheUI(project)) { projects.add(project); } } catch (CoreException e) { // Ignore closed or non-existing project } catch (InPlaceException e) { // Ignore problems getting project description } } // Get requiring projects of UI contributors if (projects.size() > 0) { ProjectSorter bs = new ProjectSorter(); Collection<IProject> reqProjects = bs.sortRequiringProjects(projects); projects.addAll(reqProjects); } return projects; } /** * Check if this project is dependent on the Eclipse UI plug-in (org.eclipse.ui) * * @param project to check for dependency on the UI plug-in * @return true if this project is dependent on the UI plug-in, otherwise false * @throws InPlaceException if project is null or failed to get the bundle project description */ public static Boolean contributesToTheUI(IProject project) throws InPlaceException { if (null == project) { throw new InPlaceException("project_null_location"); } IBundleProjectDescription bundleProjDesc = InPlace.getDefault().getBundleDescription(project); if (null == bundleProjDesc) { return false; } IRequiredBundleDescription[] bd = bundleProjDesc.getRequiredBundles(); if (null == bd) { return false; } for (int i = 0; i < bd.length; i++) { if (bd[i].getName().equals("org.eclipse.ui")) { return true; } } return false; } /** * Returns the Java project corresponding to the given project. * * @param projectName name of a given project * @return the java project or null if the given project is null * @throws InPlaceException * @throws InPlaceException if the project does not exist or is not open */ public static IJavaProject getJavaProject(String projectName) throws InPlaceException { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IProject project = root.getProject(projectName); if (null == project) { return null; } return getJavaProject(project); } /** * Returns the Java project corresponding to the given project. * * @param project a given project * @return the java project or null if the given project is null * @throws InPlaceException if the project does not exist or is not open */ public static IJavaProject getJavaProject(IProject project) throws InPlaceException { IJavaProject javaProject = null; try { if (project.hasNature(JavaCore.NATURE_ID)) { javaProject = JavaCore.create(project); } } catch (CoreException e) { throw new InPlaceException("project_not_accessible", e); } return javaProject; } /** * Convert from java projects to general projects * * @param javaProjects set of java projects * @return set of projects */ public static Collection<IProject> toProjects(Collection<IJavaProject> javaProjects) { Collection<IProject> projects = new ArrayList<IProject>(); for (IJavaProject javaProject : javaProjects) { IProject project = javaProject.getProject(); if (null != project) { projects.add(project); } } return projects; } /** * Get a project based on it's name. * * @param name a project name * @return the project or null if the given project is null */ public static IProject getProject(String name) { if (null == name) { throw new InPlaceException("project_null"); } IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IProject p = root.getProject(name); return p; } /** * Find a project based on a location identifier. * * @param location identifier string prepended with the file protocol (by value) * @param referenceScheme true if the path is by reference (path prepended with: reference:file:/) and false * (path prepended with: file:/) if by value * @return the project or null if not found */ public static IProject getProjectFromLocation(String location, boolean referenceScheme) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IWorkspaceRoot root = workspace.getRoot(); for (IProject project : root.getProjects()) { try { if (project.hasNature(JavaCore.NATURE_ID) && project.hasNature(PLUGIN_NATURE_ID) && project.isOpen()) { URL pLoc = new URL(getProjectLocationIdentifier(project, referenceScheme)); URL bLoc = new URL(location); if (Category.DEBUG) { TraceMessage.getInstance().getString("display", "Project location: " + pLoc.getPath()); TraceMessage.getInstance().getString("display", "Bundle location: " + bLoc.getPath()); } if (pLoc.getPath().equalsIgnoreCase(bLoc.getPath())) { return project; } } } catch (ProjectLocationException e) { StatusManager.getManager().handle( new BundleStatus(StatusCode.ERROR, InPlace.PLUGIN_ID, e.getLocalizedMessage(), e), StatusManager.LOG); } catch (CoreException e) { // Ignore closed or non-existing project String msg = WarnMessage.getInstance().formatString("project_missing_at_location", location); StatusManager.getManager().handle(new BundleStatus(StatusCode.WARNING, InPlace.PLUGIN_ID, msg, e), StatusManager.LOG); } catch (MalformedURLException e) { String msg = ExceptionMessage.getInstance().formatString("project_location_malformed_error", location); StatusManager.getManager().handle(new BundleStatus(StatusCode.WARNING, InPlace.PLUGIN_ID, msg, e), StatusManager.LOG); } } return null; } /** * Retrieves the project location identifier as an absolute file system path of the specified project * prepended with the reference and/or file scheme. Uses the platform-dependent path separator. This method * is used internally with the specified reference scheme set to {@code true} when bundles are installed. * <p> * After a bundle is installed the path returned from {@linkplain Bundle#getLocation()} equals the path * returned from this method with the reference scheme parameter set to {@code true}. This method use * {@linkplain IProject#getLocation()} internally. * * @param project which is the base for finding the path * @param referenceScheme true if the path is by reference (path prepended with: reference:file:/) and false * (path prepended with: file:/) if by value * @return the absolute file system path of the project prepended with the specified URI scheme * @throws ProjectLocationException if the specified project is null or the location of the specified * project could not be found * @see IProject#getLocation() * @see Bundle#getLocation() */ public static String getProjectLocationIdentifier(IProject project, Boolean referenceScheme) throws ProjectLocationException { if (null == project) { throw new ProjectLocationException("project_null_location"); } StringBuffer locScheme = null; if (referenceScheme) { locScheme = new StringBuffer(bundleReferenceLocationScheme); } else { locScheme = new StringBuffer(bundleFileLocationScheme); } IPath path = project.getLocation(); if (null == path || path.isEmpty()) { throw new ProjectLocationException("project_location_find", project.getName()); } String locIdent = path.toOSString(); return locScheme.append(locIdent).toString(); } /** * Finds all source class path entries and return the relative path of source folders * * @param project with source folders * @return source folders or an empty collection * @throws JavaModelException when accessing the project resource or the class path element does not exist * @throws InPlaceException if the project could not be accessed */ public static Collection<IPath> getJavaProjectSourceFolders(IProject project) throws JavaModelException, InPlaceException { ArrayList<IPath> paths = new ArrayList<IPath>(); IJavaProject javaProject = getJavaProject(project); IClasspathEntry[] classpathEntries = javaProject.getResolvedClasspath(true); for (int i = 0; i < classpathEntries.length; i++) { IClasspathEntry entry = classpathEntries[i]; if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE) { IPath path = entry.getPath(); String segment = path.segment(path.segmentCount() - 1); if (null != segment) { paths.add(new Path(segment)); } } } return paths; } /** * Convert from general projects to java projects * * @param projects set of general projects * @return set of java projects */ public static Collection<IJavaProject> toJavaProjects(Collection<IProject> projects) { Collection<IJavaProject> javaProjects = new ArrayList<IJavaProject>(); for (IProject project : projects) { IJavaProject javaProject = JavaCore.create(project); if (null != javaProject) { javaProjects.add(javaProject); } } return javaProjects; } /** * Check if there are build errors from the most recent build. * * @return cancel list of projects with errors or an empty list * @throws InPlaceException if one of the specified projects does not exist or is closed */ public static Collection<IProject> getBuildErrors(Collection<IProject> projects) throws InPlaceException { Collection<IProject> errors = new LinkedHashSet<IProject>(); for (IProject project : projects) { if (hasBuildErrors(project)) { errors.add(project); } } return errors; } /** * Check if the compilation state of an {@link IJavaProject} has errors. * * @param project the {@link IJavaProject} to check for errors * @return <code>true</code> if the project has compilation errors (or has never been built), * <code>false</code> otherwise * @throws InPlaceException if project does not exist or is closed */ public static boolean hasBuildErrors(IProject project) throws InPlaceException { try { if (null != project && project.isAccessible()) { IMarker[] problems = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); // check if any of these have a severity attribute that indicates an error for (int problemsIndex = 0; problemsIndex < problems.length; problemsIndex++) { if (IMarker.SEVERITY_ERROR == problems[problemsIndex].getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO)) { return true; } } } } catch (CoreException e) { throw new InPlaceException(e, "has_build_errors", project); } return false; } public static boolean hasManifestBuildErrors(IProject project) throws InPlaceException { try { if (!BundleProject.hasManifest(project)) { return true; } IMarker[] problems = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); // check if any of these have a severity attribute that indicates an error for (int problemsIndex = 0; problemsIndex < problems.length; problemsIndex++) { if (IMarker.SEVERITY_ERROR == problems[problemsIndex].getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO)) { IResource resource = problems[problemsIndex].getResource(); if (resource instanceof IFile && resource.getName().equals(MANIFEST_FILE_NAME)) { return true; } } } } catch (CoreException e) { throw new InPlaceException(e, "manifest_has_errors", project); } return false; } /** * Checks if all the projects have build state * * @param projects to check for build state * @return Set of projects missing build state among the specified projects or an empty set */ public static Collection<IProject> hasBuildState(Collection<IProject> projects) { Collection<IProject> missingBuildState = new LinkedHashSet<IProject>(); for (IProject project : projects) { if (!hasBuildState(project)) { missingBuildState.add(project); } } return missingBuildState; } /** * Checks if the project has build state * * @param project to check for build state * @return true if the project has build state, otherwise false */ public static boolean hasBuildState(IProject project) throws InPlaceException { if (null == project) { throw new InPlaceException("null_project_build_state"); } if (project.isAccessible()) { IJavaProject javaProject = getJavaProject(project.getName()); if (javaProject.hasBuildState()) { return true; } } return false; } /** * If one or more of the workspace projects have build errors, issue a warning and return the error closure. * Adds a job build error if one or more projects have build errors. * * @param projectScope set of projects to search for errors in. If scope is null or empty, search in all * activated projects * @param activated if true only consider activated bundles. If false consider all projects * @return projects with errors and their requiring projects (error closure) or an empty collection */ static public Collection<IProject> reportBuildErrorClosure(Collection<IProject> projectScope, String name) { ProjectSorter ps = new ProjectSorter(); if (null == projectScope || projectScope.size() == 0) { projectScope = getInstallableProjects(); } Collection<IProject> errorClosure = ps.getRequiringBuildErrorClosure(projectScope); String msg = formatBuildErrorsFromClosure(errorClosure, name); if (null != msg) { String warnMsg = WarnMessage.getInstance().formatString(WarnMessage.defKey, msg); StatusManager.getManager().handle(new BundleStatus(StatusCode.WARNING, InPlace.PLUGIN_ID, warnMsg), StatusManager.LOG); } return errorClosure; } /** * If one or more of the workspace projects have build errors, issue a warning and return the error closure. * Adds a job build error if one or more projects have build errors. * * @param projectScope set of projects to search for errors in. If scope is null or empty, search in all * activated projects * @param activated if true only consider activated bundles. If false consider all projects * @return projects with errors and their requiring projects (error closure) or an empty collection */ static public Collection<IProject> reportBuildErrorClosure(Collection<IProject> projectScope, Boolean activated, String name) { ProjectSorter bs = new ProjectSorter(); if (null == projectScope || projectScope.size() == 0) { if (activated) { projectScope = getActivatedProjects(); } else { projectScope = getCandidateProjects(); } } Collection<IProject> errorClosure = bs.getRequiringBuildErrorClosure(projectScope, activated); String msg = formatBuildErrorsFromClosure(errorClosure, name); if (null != msg) { String warnMsg = WarnMessage.getInstance().formatString(WarnMessage.defKey, msg); StatusManager.getManager().handle(new BundleStatus(StatusCode.WARNING, InPlace.PLUGIN_ID, warnMsg), StatusManager.LOG); } return errorClosure; } /** * Format and report projects with build errors and their requiring projects * * @param errorClosure error closures containing projects with build errors and their requiring projects * @return null if no errors, otherwise the error message */ static public String formatBuildErrorsFromClosure(Collection<IProject> errorClosure, String name) { String msg = null; if (errorClosure.size() > 0) { Collection<IProject> errorProjects = getBuildErrors(errorClosure); errorProjects.addAll(hasBuildState(errorClosure)); Collection<IProject> closure = new ArrayList<IProject>(errorClosure); closure.removeAll(errorProjects); if (closure.size() > 0) { msg = WarnMessage.getInstance().formatString("build_errors_with_requring", name, formatProjectList(errorProjects), formatProjectList(closure)); } else if (errorClosure.size() > 0) { msg = WarnMessage.getInstance().formatString("build_errors", name, formatProjectList(errorProjects)); } } return msg; } /** * A delegate for checking if auto build is enabled * * @return true if auto build is enabled, false if not */ public static Boolean isAutoBuilding() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); return workspace.isAutoBuilding(); } /** * Enables auto build * * @param autoBuild true to enable and false to disable * @return previous state of auto build */ public static Boolean setAutoBuild(Boolean autoBuild) { Boolean autoBuilding = isAutoBuilding(); IWorkspace workspace = ResourcesPlugin.getWorkspace(); try { IWorkspaceDescription desc = workspace.getDescription(); desc.setAutoBuilding(autoBuild); workspace.setDescription(desc); } catch (CoreException e) { throw new InPlaceException(e, "error_set_autobuild"); } return autoBuilding; } /** * Formats the collection as a comma separated list. * * @param projects collection of projects to format * @return a comma separated list of projects or an empty string */ public static String formatProjectList(Collection<IProject> projects) { StringBuffer sb = new StringBuffer(); if (null != projects && projects.size() >= 1) { for (Iterator<IProject> iterator = projects.iterator(); iterator.hasNext();) { IProject project = iterator.next(); sb.append(project.getName()); if (iterator.hasNext()) { sb.append(", "); } } } return sb.toString(); } }