/******************************************************************************* * Copyright © 2011, 2013 IBM Corporation 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: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.ide.core.utils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.externaltools.internal.model.BuilderCoreUtils; import org.eclipse.core.externaltools.internal.model.ExternalToolBuilder; 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.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.edt.gen.EglContext; import org.eclipse.edt.ide.core.AbstractGenerator; import org.eclipse.edt.ide.core.CoreIDEPluginStrings; import org.eclipse.edt.ide.core.EDTCoreIDEPlugin; import org.eclipse.edt.ide.core.EDTRuntimeContainer; import org.eclipse.edt.ide.core.IGenerator; import org.eclipse.edt.ide.core.model.IEGLProject; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities; import org.eclipse.osgi.util.NLS; /** * Utility methods for working within Eclipse. */ @SuppressWarnings("restriction") public class EclipseUtilities { private EclipseUtilities() { // No instances. } /** * @return true if the outputFolder represents a folder within the workspace */ public static boolean shouldWriteFileInEclipse( String outputFolder ) { return !new Path(outputFolder).isAbsolute(); } /** * Returns the container in which the file should be written. * * @param outputFolder The folder in which to generate, in the internal folder format (see {@link #convertGenerationDirectoryToPath(String)}) * @param eglFile The source file being generated. * @param relativeFilePath The path of the file to be written inside outputFolder. e.g. my/pkg/Foo.java * @return the container in which the file should be written. * @throws CoreException */ public static IContainer getOutputContainer(String outputFolder, IFile eglFile, String relativeFilePath) throws CoreException { IContainer container = null; IPath outputFolderPath = new Path(convertFromInternalPath(outputFolder)); int lastSlash = relativeFilePath.lastIndexOf('/'); if (outputFolderPath.isAbsolute()) { IPath path = outputFolderPath; if (lastSlash != -1) { path = path.append(relativeFilePath.substring(0, lastSlash)); } if (path.segmentCount() > 1) { container = ResourcesPlugin.getWorkspace().getRoot().getFolder(path); // Verify the containing project exists if (!container.getProject().isAccessible()) { throw new CoreException(new Status(IStatus.ERROR, EDTCoreIDEPlugin.PLUGIN_ID, NLS.bind(CoreIDEPluginStrings.ProjectNotAccessible, container.getProject().getName()))); } } else if (path.segmentCount() == 1){ container = ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0)); // Projects must exist, we can't just create them like folders if (!container.isAccessible()) { throw new CoreException(new Status(IStatus.ERROR, EDTCoreIDEPlugin.PLUGIN_ID, NLS.bind(CoreIDEPluginStrings.ProjectNotAccessible, container.getName()))); } } else { throw new CoreException(new Status(IStatus.ERROR, EDTCoreIDEPlugin.PLUGIN_ID, NLS.bind(CoreIDEPluginStrings.CouldNotGetOutputFolder, outputFolderPath))); } } else { // Relative to the source project if (lastSlash == -1) { container = outputFolderPath.segmentCount() == 0 ? eglFile.getProject() : eglFile.getProject().getFolder(outputFolderPath); } else { container = eglFile.getProject().getFolder(outputFolderPath.append(relativeFilePath.substring(0, lastSlash))); } } return container; } /** * Returns the path that should be used for the output file. * * @param relativeFilePath The path of the file to be written inside outputFolder. e.g. my/pkg/Foo.java * @return the path that should be used for the output file. */ public static IPath getOutputFilePath(String relativeFilePath) { int lastSlash = relativeFilePath.lastIndexOf('/'); String fileName = lastSlash == -1 ? relativeFilePath : relativeFilePath.substring(lastSlash + 1); return new Path(fileName); } /** * Writes a file using Eclipse API, so that the Eclipse filesystem is kept in sync. * * @param outputFolder The folder in which to generate, in the internal folder format (see {@link #convertGenerationDirectoryToPath(String)}) * @param eglFile The source file being generated. * @param contentsToWrite The content of the file to write. * @param relativeFilePath The path of the file to be written inside outputFolder. e.g. my/pkg/Foo.java * @return the file that was written * @throws CoreException */ public static IFile writeFileInEclipse(String outputFolder, IFile eglFile, String contentsToWrite, String relativeFilePath) throws CoreException { IPath filePath = getOutputFilePath(relativeFilePath); IContainer container = getOutputContainer(outputFolder, eglFile, relativeFilePath); writeFileInEclipse(container, filePath, EclipseUtilities.getInputStream(eglFile, contentsToWrite), true); return container.getFile(filePath); } /** * Returns a handle to the generated output file for the given path. * * @param outputFolder The folder in which to generate, in the internal folder format (see {@link #convertGenerationDirectoryToPath(String)}) * @param eglFile The source file being generated. * @param relativeFilePath The path of the file to be written inside outputFolder. e.g. my/pkg/Foo.java * @return a handle to the generated output file for the given path. * @throws CoreException */ public static IFile getOutputFile(String outputFolder, IFile eglFile, String relativeFilePath) throws CoreException { IPath filePath = getOutputFilePath(relativeFilePath); IContainer container = getOutputContainer(outputFolder, eglFile, relativeFilePath); return container.getFile(filePath); } /** * Writes a file using Eclipse API, so that the Eclipse filesystem is kept in sync. The input stream will be closed before the method returns. * * @param outputContainer The location in which to write the file. * @param fileName The name of the file to write. * @param contents The contents to write to the file. * @param createFoldersIfNecessary Flag indicating if we should create parent folders if they don't already exist. * @throws CoreException */ public static void writeFileInEclipse(IContainer outputContainer, IPath fileName, InputStream dataStream, boolean createFoldersIfNecessary) throws CoreException { try { if (createFoldersIfNecessary && outputContainer instanceof IFolder) { createFolder((IFolder)outputContainer); } IFile outputFile = outputContainer.getFile(fileName); if (outputFile.exists()) { outputFile.setContents(dataStream, true, false, null); } else { outputFile.create(dataStream, IResource.FORCE, null); } } finally { if (dataStream != null) { try { dataStream.close(); } catch (IOException e) { } } } } /** * Creates the folder and its parents if they don't already exist. * * @param folder The folder to create. * @throws CoreException */ public static void createFolder(IFolder folder) throws CoreException { if (!folder.exists()) { IContainer parent = folder.getParent(); if (parent instanceof IFolder) { createFolder((IFolder)parent); } folder.create(true, true, null); folder.setDerived(true, null); } } /** * Creates an InputStream for the data, attempting to use the charset of the source file, if available. * * @param sourceFile The source file from which to get the charset (may be null). * @param data The data the input stream will write. * @return an InputStream for the data, attempting to use the charset of the source file, if available. */ public static InputStream getInputStream(IFile sourceFile, String data) { if (sourceFile != null) { try { String encoding = sourceFile.getCharset(); if (encoding != null) { try { return new ByteArrayInputStream(data.getBytes(encoding)); } catch (UnsupportedEncodingException e) { } } } catch (CoreException e) { } } return new ByteArrayInputStream(data.getBytes()); } /** * Adds the outputFolder as a Java source folder if the project is a Java project. * * @param project The project containing the folder (used when outputFolder is a relative path) * @param outputFolder The path of the folder. It may be project-relative, or workspace-relative. If workspace-relative * it should start with 'F/' for a folder or 'P/' for a project. * @param forceClasspathRefresh A classpath needs to be refreshed if an entry already exists for the output folder, but the folder has yet to be * created. This can occur when a project is exported without a generation directory. * @throws CoreException */ public static void addToJavaBuildPathIfNecessary(IProject project, String outputFolder, boolean forceClasspathRefresh) throws CoreException { if (project.hasNature(JavaCore.NATURE_ID)) { IJavaProject javaProject = JavaCore.create(project); if (javaProject.exists()) { IClasspathEntry[] entries = javaProject.getRawClasspath(); IPath outputFolderPath = new Path(convertFromInternalPath(outputFolder)); boolean needToAdd = true; IPath fullPath = outputFolderPath.isAbsolute() ? outputFolderPath : outputFolderPath.segmentCount() == 0 ? project.getFullPath() : project.getFolder(outputFolderPath).getFullPath(); for (int i = 0; i < entries.length; i++) { if (entries[i].getEntryKind() == IClasspathEntry.CPE_SOURCE) { IPath nextPath = entries[i].getPath(); // JDT throws an error if you have a source folder within a source folder. We could add exclusions to support this, but // for now we just won't add the folder. if (nextPath.isPrefixOf(fullPath) || fullPath.isPrefixOf(nextPath)) { needToAdd = false; break; } } } if (needToAdd){ IClasspathEntry[] newEntries = new IClasspathEntry[entries.length + 1]; System.arraycopy(entries, 0, newEntries, 0, entries.length); newEntries[newEntries.length - 1] = JavaCore.newSourceEntry(fullPath); javaProject.setRawClasspath(newEntries, null); } if (!needToAdd && forceClasspathRefresh){ javaProject.setRawClasspath(javaProject.readRawClasspath(), javaProject.readOutputLocation(), null); } } } } /** * Returns a path representing the generation directory, which may be worspace- or project-relative. An internal * convention is used for paths and this routine will normalize it to be of the format "/myproject/my/folder" for * workspace-relative paths and "my/folder" for project-relative paths. A blank string is returned for "this project". * * @param path The internal representation of the generation directory path * @return the normalized generation directory path */ public static String convertFromInternalPath(String path) { // We prefix workspace-relative paths with W/, project-relative paths with P/. if (path.startsWith("W/")) { //$NON-NLS-1$ return path.substring(1); } else if (path.startsWith("P/")) { //$NON-NLS-1$ return path.substring(2); } return path; } public static String convertToInternalPath(String path) { if (path.startsWith("/")) { //$NON-NLS-1$ return "W" + path; //$NON-NLS-1$ } return "P/" + path; //$NON-NLS-1$ } /** * Adds the runtime containers to the project if necessary. This does nothing if the project is * not a Java project. * * @param project The Java project. * @param generator The generator provider. * @param ctx The generation context. */ public static void addRuntimesToProject(IProject project, IGenerator generator, EglContext ctx) { EDTRuntimeContainer[] baseRuntimes = generator instanceof AbstractGenerator ? ((AbstractGenerator)generator).resolveBaseRuntimeContainers() : null; EDTRuntimeContainer[] containersToAdd; Set<String> requiredContainers = ctx.getRequiredRuntimeContainers(); if (requiredContainers.size() == 0) { if (baseRuntimes == null || baseRuntimes.length == 0) { return; } containersToAdd = baseRuntimes; } else { Set<EDTRuntimeContainer> containers = new HashSet<EDTRuntimeContainer>(10); if (baseRuntimes != null && baseRuntimes.length > 0) { containers.addAll(Arrays.asList(baseRuntimes)); } for (EDTRuntimeContainer container : generator.getRuntimeContainers()) { if (requiredContainers.contains(container.getId())) { containers.add(container); } } containersToAdd = containers.toArray(new EDTRuntimeContainer[containers.size()]); } if (containersToAdd == null || containersToAdd.length == 0) { return; } try { if (project.hasNature(JavaCore.NATURE_ID)) { IJavaProject javaProject = JavaCore.create(project); IClasspathEntry[] classpath = javaProject.getRawClasspath(); List<IClasspathEntry> additions = new ArrayList<IClasspathEntry>(); for (int i = 0; i < containersToAdd.length; i++) { IPath path = containersToAdd[i].getPath(); boolean found = false; for (int j = 0; j < classpath.length; j++) { if (classpath[j].getEntryKind()== IClasspathEntry.CPE_CONTAINER && classpath[j].getPath().equals(path)) { found = true; break; } } if (!found) { additions.add(JavaCore.newContainerEntry(path)); } } if (additions.size() > 0) { IClasspathEntry[] newEntries = new IClasspathEntry[classpath.length + additions.size()]; System.arraycopy(classpath, 0, newEntries, 0, classpath.length); for (int i = 0; i < additions.size(); i++) { newEntries[classpath.length + i] = additions.get(i); } javaProject.setRawClasspath(newEntries, null); } } } catch (CoreException e) { EDTCoreIDEPlugin.log(e); } } /** * Adds the SMAP builder to the project if necessary. This does nothing if the project is not a Java project. * @param project The Java project. */ public static void addSMAPBuilder(IProject project) { try { if (project.hasNature(JavaCore.NATURE_ID)) { String builderID = "org.eclipse.edt.debug.core.smapBuilder"; IProjectDescription description = project.getDescription(); ICommand smapCommand = null; ICommand[] commands = description.getBuildSpec(); for (ICommand command : commands) { if (command.getBuilderName().equals(builderID)) { smapCommand = command; break; } // Disabled builders become "external tool builders" else if (ExternalToolBuilder.ID.equals(command.getBuilderName())) { Object attr = command.getArguments().get(BuilderCoreUtils.LAUNCH_CONFIG_HANDLE); if (attr instanceof String && ((String)attr).contains(builderID)) { smapCommand = command; break; } } } if (smapCommand == null) { smapCommand = description.newCommand(); smapCommand.setBuilderName(builderID); ICommand[] newCommands = new ICommand[commands.length + 1]; System.arraycopy(commands, 0, newCommands, 0, commands.length); newCommands[commands.length] = smapCommand; description.setBuildSpec(newCommands); project.setDescription(description, null); } } } catch (CoreException e) { EDTCoreIDEPlugin.log(e); } } /** * Returns true if this project is a J2EE web project. Returns false if this * is not a web project or is a static web project. * * @param project * Project * @return boolean */ public static boolean isWebProject(IProject project) { return JavaEEProjectUtilities.isDynamicWebProject(project); } /** * Get the source folder name. For new Java projects, check the source * folder name in the Java Build Path preferences. For all types of Java * projects, use the first source folder from the classpath. * * @param myProject * The project where the folder for Java source resides. * * @return String The Java source folder name. */ public static String getJavaSourceFolderName(IProject project) { String folderName = null; try { if ( project.hasNature( JavaCore.NATURE_ID ) ) { // Use the first folder from the project's classpath. IJavaProject javaProject = JavaCore.create( project ); IClasspathEntry[] entries = javaProject.getRawClasspath(); for ( int i = 0; i < entries.length; i++ ) { IClasspathEntry entry = entries[ i ]; if ( entry.getEntryKind() == IClasspathEntry.CPE_SOURCE ) { // Get the folder from the entry's path. The project name // needs to be removed from the path before this will work. IPath path = entry.getPath().removeFirstSegments( 1 ); return path.toString(); } } } } catch (Exception e) { } return folderName; } public static List<String> getDependentDescriptors( IProject project ) throws Exception { List eglProjectPath = org.eclipse.edt.ide.core.internal.utils.Util.getEGLProjectPath(project); final List<String> egldds = new ArrayList<String>(); for (Iterator<IEGLProject> iter1 = eglProjectPath.iterator(); iter1.hasNext();) { IEGLProject eglProject = iter1.next(); IProject dependentPro = eglProject.getProject(); final IPath outputPath = eglProject.getOutputLocation(); dependentPro.accept( new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { if (outputPath.isPrefixOf(resource.getFullPath())) { if ( resource instanceof IFile && "egldd".equals( resource.getFileExtension() ) ) { try { egldds.add( resource.getFullPath().toString() ); } catch (Exception e) { } return false; } } return resource.getFullPath().isPrefixOf(outputPath); } }); } return egldds; } }