// COPIED from spring-ide org.springframework.ide.eclipse.core.SpringCoreUtils /******************************************************************************* * Copyright (c) 2012, 2013 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.core; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.LinkedHashSet; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.commons.lang.StringUtils; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IPathVariableManager; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ProjectScope; 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.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.wst.common.project.facet.core.FacetedProjectFramework; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; import org.springsource.ide.eclipse.commons.internal.core.CorePlugin; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Some helper methods. * @author Torsten Juergeleit * @author Christian Dupuis * @author Martin Lippert */ public final class SpringCoreUtils { /** * File name of OSGi bundle manifests * @since 2.0.5 */ public static String BUNDLE_MANIFEST_FILE = "MANIFEST.MF"; /** * Folder name of OSGi bundle manifests directories * @since 2.0.5 */ public static String BUNDLE_MANIFEST_FOLDER = "META-INF"; /** New placeholder string for Spring 3 EL support */ public static final String EL_PLACEHOLDER_PREFIX = "#{"; /** URL file schema */ public static final String FILE_SCHEME = "file"; public static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static final String PLACEHOLDER_PREFIX = "${"; public static final String PLACEHOLDER_SUFFIX = "}"; public static final String SOURCE_CONTROL_SCHEME = "sourcecontrol"; private static final String DEPLOY_PATH = "deploy-path"; private static boolean DOCUMENT_BUILDER_ERROR = false; private static final Object DOCUMENT_BUILDER_LOCK = new Object(); private static XPathExpression EXPRESSION; private static boolean SAX_PARSER_ERROR = false; private static final Object SAX_PARSER_LOCK = new Object(); private static final String SOURCE_PATH = "source-path"; private static final String XPATH_EXPRESSION = "//project-modules/wb-module/wb-resource"; private static final String SPRING_BUILDER_ID = "org.springframework.ide.eclipse.core.springbuilder"; private static final String MARKER_ID = "org.springframework.ide.eclipse.core.problemmarker"; public static final String NATURE_ID = "org.springframework.ide.eclipse.core.springnature"; static { try { XPathFactory newInstance = XPathFactory.newInstance(); XPath xpath = newInstance.newXPath(); EXPRESSION = xpath.compile(XPATH_EXPRESSION); } catch (XPathExpressionException e) { throw new RuntimeException(e); } } /** * Adds given builder to specified project. */ public static void addProjectBuilder(IProject project, String builderID, IProgressMonitor monitor) throws CoreException { IProjectDescription desc = project.getDescription(); ICommand builderCommand = getProjectBuilderCommand(desc, builderID); if (builderCommand == null) { // Add a new build spec ICommand command = desc.newCommand(); command.setBuilderName(builderID); // Commit the spec change into the project addProjectBuilderCommand(desc, command); project.setDescription(desc, monitor); } } /** * Adds (or updates) a builder in given project description. */ public static void addProjectBuilderCommand(IProjectDescription description, ICommand command) throws CoreException { ICommand[] oldCommands = description.getBuildSpec(); ICommand oldBuilderCommand = getProjectBuilderCommand(description, command.getBuilderName()); ICommand[] newCommands; if (oldBuilderCommand == null) { // Add given builder to the end of the builder list newCommands = new ICommand[oldCommands.length + 1]; System.arraycopy(oldCommands, 0, newCommands, 0, oldCommands.length); newCommands[oldCommands.length] = command; } else { // Replace old builder with given new one for (int i = 0, max = oldCommands.length; i < max; i++) { if (oldCommands[i] == oldBuilderCommand) { oldCommands[i] = command; break; } } newCommands = oldCommands; } description.setBuildSpec(newCommands); } /** * Adds given nature as first nature to specified project. */ public static void addProjectNature(IProject project, String nature, IProgressMonitor monitor) throws CoreException { if (project != null && nature != null) { if (!project.hasNature(nature)) { IProjectDescription desc = project.getDescription(); String[] oldNatures = desc.getNatureIds(); String[] newNatures = new String[oldNatures.length + 1]; newNatures[0] = nature; if (oldNatures.length > 0) { System.arraycopy(oldNatures, 0, newNatures, 1, oldNatures.length); } desc.setNatureIds(newNatures); project.setDescription(desc, monitor); } } } /** * Triggers a build of the given {@link IProject} instance, but only the * Spring builder * @param project the project to build */ public static void buildProject(IProject project) { buildProject(project, SPRING_BUILDER_ID); } /** * Triggers a build of the given {@link IProject} instance with a full build * and all builders * @param project the project to build */ public static void buildFullProject(IProject project) { buildProject(project, null); } /** * Triggers a build of the given {@link IProject} instance, but only the * Spring builder * @param project the project to build * @param builderID the ID of the specific builder that should be executed * on the project * @since 3.2.0 */ public static void buildProject(IProject project, String builderID) { if (ResourcesPlugin.getWorkspace().isAutoBuilding()) { scheduleBuildInBackground(project, ResourcesPlugin.getWorkspace().getRuleFactory().buildRule(), new Object[] { ResourcesPlugin.FAMILY_AUTO_BUILD }, builderID); } } /** * Creates given folder and (if necessary) all of it's parents. */ public static void createFolder(IFolder folder, IProgressMonitor monitor) throws CoreException { if (!folder.exists()) { IContainer parent = folder.getParent(); if (parent instanceof IFolder) { createFolder((IFolder) parent, monitor); } folder.create(true, true, monitor); } } /** * Creates specified simple project. */ public static IProject createProject(String projectName, IProjectDescription description, IProgressMonitor monitor) throws CoreException { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IProject project = root.getProject(projectName); if (!project.exists()) { if (description == null) { project.create(monitor); } else { project.create(description, monitor); } } else { project.refreshLocal(IResource.DEPTH_INFINITE, monitor); } if (monitor.isCanceled()) { throw new OperationCanceledException(); } if (!project.isOpen()) { project.open(monitor); } return project; } /** * Finds the first line separator used by the given text. * @return </code>"\n"</code> or </code>"\r"</code> or </code>"\r\n"</code>, * or <code>null</code> if none found * @since 2.2.2 */ public static String findLineSeparator(char[] text) { // find the first line separator int length = text.length; if (length > 0) { char nextChar = text[0]; for (int i = 0; i < length; i++) { char currentChar = nextChar; nextChar = i < length - 1 ? text[i + 1] : ' '; switch (currentChar) { case '\n': return "\n"; //$NON-NLS-1$ case '\r': return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$ } } } // not found return null; } /** * Returns the specified adapter for the given object or <code>null</code> * if adapter is not supported. */ @SuppressWarnings("unchecked") public static <T> T getAdapter(Object object, Class<T> adapter) { if (object != null && adapter != null) { if (adapter.isAssignableFrom(object.getClass())) { return (T) object; } if (object instanceof IAdaptable) { return (T) ((IAdaptable) object).getAdapter(adapter); } } return null; } public static IFile getDeploymentDescriptor(IProject project) { if (SpringCoreUtils.hasProjectFacet(project, "jst.web")) { IFile settingsFile = project.getFile(".settings/org.eclipse.wst.common.component"); if (settingsFile.exists()) { try { NodeList nodes = (NodeList) EXPRESSION .evaluate(parseDocument(settingsFile), XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { Element element = (Element) nodes.item(i); if ("/".equals(element.getAttribute(DEPLOY_PATH))) { String path = element.getAttribute(SOURCE_PATH); if (path != null) { IFile deploymentDescriptor = project.getFile(new Path(path).append("WEB-INF").append( "web.xml")); if (deploymentDescriptor.exists()) { return deploymentDescriptor; } } } } } catch (Exception e) { StatusHandler.log(new Status(IStatus.WARNING, CorePlugin.PLUGIN_ID, 1, e.getMessage(), e)); } } } return null; } public static DocumentBuilder getDocumentBuilder() { try { DocumentBuilderFactory documentBuilderFactory = getDocumentBuilderFactory(); documentBuilderFactory.setExpandEntityReferences(false); return documentBuilderFactory.newDocumentBuilder(); } catch (Exception e) { StatusHandler.log(new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID, "Error creating DocumentBuilder", e)); } return null; } public static DocumentBuilderFactory getDocumentBuilderFactory() { if (!DOCUMENT_BUILDER_ERROR) { try { // this might fail on IBM J9; therefore trying only once and // then falling back to // OSGi service reference as it should be return DocumentBuilderFactory.newInstance(); } catch (Exception e) { StatusHandler.log(new Status(IStatus.INFO, CorePlugin.PLUGIN_ID, "Error creating DocumentBuilderFactory. Switching to OSGi service reference.")); DOCUMENT_BUILDER_ERROR = true; } } BundleContext bundleContext = CorePlugin.getDefault().getBundle().getBundleContext(); ServiceReference reference = bundleContext.getServiceReference(DocumentBuilderFactory.class.getName()); if (reference != null) { try { synchronized (DOCUMENT_BUILDER_LOCK) { return (DocumentBuilderFactory) bundleContext.getService(reference); } } catch (Exception e) { StatusHandler.log(new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID, "Error creating DocumentBuilderFactory", e)); } finally { bundleContext.ungetService(reference); } } return null; } /** * Returns the line separator found in the given text. If it is null, or not * found return the line delimiter for the given project. If the project is * null, returns the line separator for the workspace. If still null, return * the system line separator. * @since 2.2.2 */ public static String getLineSeparator(String text, IProject project) { String lineSeparator = null; // line delimiter in given text if (text != null && text.length() != 0) { lineSeparator = findLineSeparator(text.toCharArray()); if (lineSeparator != null) { return lineSeparator; } } // line delimiter in project preference IScopeContext[] scopeContext; if (project != null) { scopeContext = new IScopeContext[] { new ProjectScope(project) }; lineSeparator = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); if (lineSeparator != null) { return lineSeparator; } } // line delimiter in workspace preference scopeContext = new IScopeContext[] { new InstanceScope() }; lineSeparator = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); if (lineSeparator != null) { return lineSeparator; } // system line delimiter return LINE_SEPARATOR; } /** * Returns specified builder from given project description. */ public static ICommand getProjectBuilderCommand(IProjectDescription description, String builderID) throws CoreException { ICommand[] commands = description.getBuildSpec(); for (int i = commands.length - 1; i >= 0; i--) { if (commands[i].getBuilderName().equals(builderID)) { return commands[i]; } } return null; } public static IPath getProjectLocation(IProject project) { return (project.getRawLocation() != null ? project.getRawLocation() : project.getLocation()); } public static URI getResourceURI(IResource resource) { if (resource != null) { URI uri = resource.getRawLocationURI(); if (uri == null) { uri = resource.getLocationURI(); } if (uri != null) { String scheme = uri.getScheme(); if (FILE_SCHEME.equalsIgnoreCase(scheme)) { return uri; } else if (SOURCE_CONTROL_SCHEME.equals(scheme)) { // special case of Rational Team Concert IPath path = resource.getLocation(); File file = path.toFile(); if (file.exists()) { return file.toURI(); } } else { IPathVariableManager variableManager = ResourcesPlugin.getWorkspace().getPathVariableManager(); return variableManager.resolveURI(uri); } } } return null; } public static SAXParser getSaxParser() { if (!SAX_PARSER_ERROR) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); SAXParser parser = factory.newSAXParser(); return parser; } catch (Exception e) { StatusHandler.log(new Status(IStatus.INFO, CorePlugin.PLUGIN_ID, "Error creating SaxParserFactory. Switching to OSGI service reference.")); SAX_PARSER_ERROR = true; } } BundleContext bundleContext = CorePlugin.getDefault().getBundle().getBundleContext(); ServiceReference reference = bundleContext.getServiceReference(SAXParserFactory.class.getName()); if (reference != null) { try { synchronized (SAX_PARSER_LOCK) { SAXParserFactory factory = (SAXParserFactory) bundleContext.getService(reference); return factory.newSAXParser(); } } catch (Exception e) { StatusHandler .log(new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID, "Error creating SaxParserFactory", e)); } finally { bundleContext.ungetService(reference); } } return null; } /** * Returns a list of all projects with the Spring project nature. */ public static Set<IProject> getSpringProjects() { Set<IProject> projects = new LinkedHashSet<IProject>(); for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { if (isSpringProject(project)) { projects.add(project); } } return projects; } /** * Returns true if given resource's project has the given nature. */ public static boolean hasNature(IResource resource, String natureId) { if (resource != null && resource.isAccessible()) { IProject project = resource.getProject(); if (project != null) { try { return project.hasNature(natureId); } catch (CoreException e) { StatusHandler.log(new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID, "An error occurred inspecting project nature", e)); } } } return false; } /** * Returns <code>true</code> if given text contains a placeholder, e.g. * <code>${beansRef}</code> . */ public static boolean hasPlaceHolder(String text) { if (text == null || StringUtils.isBlank(text)) { return false; } int pos = text.indexOf(PLACEHOLDER_PREFIX); int elPos = text.indexOf(EL_PLACEHOLDER_PREFIX); return ((pos != -1 || elPos != -1) && text.indexOf(PLACEHOLDER_SUFFIX, pos) != -1); } public static boolean hasProjectFacet(IResource resource, String facetId) { if (resource != null && resource.isAccessible()) { try { return JdtUtils.isJavaProject(resource) && FacetedProjectFramework.hasProjectFacet(resource.getProject(), facetId); } catch (CoreException e) { // TODO CD handle exception } } return false; } /** * Returns true if Eclipse's runtime bundle has the same or a newer than * given version. */ public static boolean isEclipseSameOrNewer(int majorVersion, int minorVersion) { Bundle bundle = Platform.getBundle(Platform.PI_RUNTIME); if (bundle != null) { String versionString = (String) bundle.getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION); try { Version version = new Version(versionString); int major = version.getMajor(); if (major > majorVersion) { return true; } if (major == majorVersion) { int minor = version.getMinor(); if (minor >= minorVersion) { return true; } } } catch (IllegalArgumentException e) { // ignore this exception as this can't occur in pratice } } return false; } /** * Checks if the given {@link IResource} is a OSGi bundle manifest. * <p> * Note: only the name and last segment of the folder name are checked. * @since 2.0.5 */ public static boolean isManifest(IResource resource) { // check if it is a MANIFEST.MF file in META-INF if (resource != null // && resource.isAccessible() && resource.getType() == IResource.FILE && resource.getName().equals(BUNDLE_MANIFEST_FILE) && resource.getParent() != null && resource.getParent().getProjectRelativePath() != null && resource.getParent().getProjectRelativePath().lastSegment() != null && resource.getParent().getProjectRelativePath().lastSegment().equals(BUNDLE_MANIFEST_FOLDER)) { // check if the manifest is not in an output folder IPath filePath = resource.getFullPath(); IJavaProject javaProject = JdtUtils.getJavaProject(resource); if (javaProject != null) { try { IPath defaultOutputLocation = javaProject.getOutputLocation(); if (defaultOutputLocation != null && defaultOutputLocation.isPrefixOf(filePath)) { return false; } for (IClasspathEntry entry : javaProject.getRawClasspath()) { if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { IPath outputLocation = entry.getOutputLocation(); if (outputLocation != null && outputLocation.isPrefixOf(filePath)) { return false; } } } } catch (JavaModelException e) { // don't care here } return true; } else { // if the project is not a java project -> it is the manifest return true; } } return false; } /** * Returns true if given resource's project is a Spring project. */ public static boolean isSpringProject(IResource resource) { return hasNature(resource, NATURE_ID); } /** * Returns true if Eclipse's runtime bundle has the same or a newer than * given version. */ public static boolean isVersionSameOrNewer(String versionString, int majorVersion, int minorVersion, int microVersion) { return new Version(versionString).compareTo(new Version(majorVersion, minorVersion, microVersion)) >= 0; } public static Document parseDocument(IFile deploymentDescriptor) { try { if (getResourceURI(deploymentDescriptor) != null) { return parseDocument(getResourceURI(deploymentDescriptor)); } return getDocumentBuilder().parse(new InputSource(deploymentDescriptor.getContents())); } catch (SAXException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (CoreException e) { throw new RuntimeException(e); } } public static Document parseDocument(URI deploymentDescriptor) { try { return getDocumentBuilder().parse(deploymentDescriptor.toString()); } catch (SAXException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } /** * Removes given builder from specified project. */ public static void removeProjectBuilder(IProject project, String builderID, IProgressMonitor monitor) throws CoreException { if (project != null && builderID != null) { IProjectDescription desc = project.getDescription(); ICommand[] commands = desc.getBuildSpec(); for (int i = commands.length - 1; i >= 0; i--) { if (commands[i].getBuilderName().equals(builderID)) { ICommand[] newCommands = new ICommand[commands.length - 1]; System.arraycopy(commands, 0, newCommands, 0, i); System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1); // Commit the spec change into the project desc.setBuildSpec(newCommands); project.setDescription(desc, monitor); break; } } } } /** * Removes given nature from specified project. */ public static void removeProjectNature(IProject project, String nature, IProgressMonitor monitor) throws CoreException { if (project != null && nature != null) { if (project.exists() && project.hasNature(nature)) { // first remove all problem markers (including the // inherited ones) from Spring beans project if (nature.equals(NATURE_ID)) { project.deleteMarkers(MARKER_ID, true, IResource.DEPTH_INFINITE); } // now remove project nature IProjectDescription desc = project.getDescription(); String[] oldNatures = desc.getNatureIds(); String[] newNatures = new String[oldNatures.length - 1]; int newIndex = oldNatures.length - 2; for (int i = oldNatures.length - 1; i >= 0; i--) { if (!oldNatures[i].equals(nature)) { newNatures[newIndex--] = oldNatures[i]; } } desc.setNatureIds(newNatures); project.setDescription(desc, monitor); } } } /** * Verify that file can safely be modified; eventually checkout the file * from source code control. * @return <code>true</code> if resource can be modified * @since 2.2.9 */ public static boolean validateEdit(IFile... files) { for (IFile file : files) { if (!file.exists()) { return false; } } IStatus status = ResourcesPlugin.getWorkspace().validateEdit(files, IWorkspace.VALIDATE_PROMPT); if (status.isOK()) { return true; } return false; } private static void scheduleBuildInBackground(final IProject project, ISchedulingRule rule, final Object[] jobFamilies, final String builderID) { Job job = new Job("Building workspace") { @Override public boolean belongsTo(Object family) { if (jobFamilies == null || family == null) { return false; } for (Object jobFamilie : jobFamilies) { if (family.equals(jobFamilie)) { return true; } } return false; } @Override public IStatus run(IProgressMonitor monitor) { try { if (builderID != null) { project.build(IncrementalProjectBuilder.FULL_BUILD, builderID, null, monitor); } else { project.build(IncrementalProjectBuilder.FULL_BUILD, monitor); } return Status.OK_STATUS; } catch (CoreException e) { return new Status(Status.ERROR, CorePlugin.PLUGIN_ID, 1, "Error during build of project [" + project.getName() + "]", e); } } }; if (rule != null) { job.setRule(rule); } job.setPriority(Job.BUILD); job.schedule(); } }