/****************************************************************************** * Copyright (c) 2010-2013, Linagora * * 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: * Linagora - initial API and implementation *******************************************************************************/ package com.ebmwebsourcing.petals.common.internal.provisional.utils; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; 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.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.ui.jarpackager.IJarExportRunnable; import org.eclipse.jdt.ui.jarpackager.JarPackageData; import com.ebmwebsourcing.petals.common.internal.PetalsCommonPlugin; /** * @author Vincent Zurczak - EBM WebSourcing */ public final class JavaUtils { /** * Private constructor for utility class. */ private JavaUtils() { // nothing } /** * Get the referenced projects from a Java project. * <p>The result includes the argument project.</p> * * @param javaProject * @return */ public static List<IJavaProject> getJavaProjectDependencies( IJavaProject javaProject ) { if( javaProject == null ) return Collections.emptyList(); List<IJavaProject> result = new ArrayList<IJavaProject> (); result.add( javaProject ); String[] projectNames; try { projectNames = javaProject.getRequiredProjectNames(); } catch( JavaModelException e1 ) { PetalsCommonPlugin.log( e1, IStatus.WARNING ); projectNames = new String[ 0 ]; } for( String projectName : projectNames ) { IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject( projectName ); try { if( !project.exists() || !project.isOpen() || !project.hasNature( JavaCore.NATURE_ID )) continue; } catch( CoreException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); continue; } IJavaProject p = JavaCore.create( project ); result.add( p ); } return result; } /** * Updates the class path of a Java project with libraries embedded by the studio. * @param jp the Java project * @param folders the folder names * @throws IOException * @throws JavaModelException */ public static void updateClasspathWithProjectLibraries( IJavaProject jp, IProgressMonitor monitor, String... folders ) throws IOException, JavaModelException { if( folders == null ) return; // Keep the current entries ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry> (); entries.addAll( Arrays.asList( jp.getRawClasspath())); FilenameFilter filter = new FilenameFilter() { @Override public boolean accept( File dir, String name ) { return name.endsWith( ".jar" ) || name.endsWith( ".zip" ); } }; for( String folder : folders ) { File pojoLibPath = ResourceUtils.getPluginBinaryPath( "com.ebmwebsourcing.petals.libs.esb", folder ); //$NON-NLS-1$ if( pojoLibPath == null ) { PetalsCommonPlugin.log( "Could not find the Petals libraries in the distribution.", IStatus.ERROR ); throw new IOException( "Petals libraries could not be located." ); } // Add the libraries in the project class path File[] jarFiles = pojoLibPath.listFiles( filter ); if( jarFiles != null ) { for( File jarFile : jarFiles ) { IPath path = new Path( jarFile.getAbsolutePath()); IClasspathEntry entry = JavaCore.newLibraryEntry( path, null, null ); entries.add( entry ); } } } IClasspathEntry[] newEntries = CollectionUtils.convertToArray( entries, IClasspathEntry.class ); if( ! jp.hasClasspathCycle( newEntries )) jp.setRawClasspath( newEntries, monitor ); } /** * Makes an Eclipse project a Java project. * <p> * If the project does not exist, or is not accessible, then nothing is done.<br /> * If the project does not have the Java nature, this nature is added. * </p> * <p> * The default output directory is "bin". If this directory does not exist, it is created. * If the creation fails, then no output directory is set. * </p> * <p> * The default source directory is "src/main/java". If this directory does not exist, it is created. * If the creation fails, then no source directory is set. * </p> * * @param project the project to transform into a Java project * @return the created Java project * @throws CoreException if something went wrong */ public static IJavaProject createJavaProject( IProject project ) throws CoreException { IJavaProject jp = null; if( project.isAccessible()) { // Add the Java nature? if( ! project.hasNature( JavaCore.NATURE_ID )) { IProjectDescription description = project.getDescription(); String[] natures = description.getNatureIds(); String[] newNatures = new String[ natures.length + 1 ]; System.arraycopy( natures, 0, newNatures, 0, natures.length ); newNatures[ natures.length ] = JavaCore.NATURE_ID; description.setNatureIds( newNatures ); project.setDescription( description, null ); } // Set the default class path jp = JavaCore.create( project ); IProgressMonitor monitor = new NullProgressMonitor(); // // Output location IPath ppath = project.getFullPath(); IPath binPath = ppath.append( PetalsConstants.LOC_BIN_FOLDER ); File binFile = ResourcesPlugin.getWorkspace().getRoot().getLocation().append( binPath ).toFile(); if(binFile.exists() && binFile.isDirectory() || ! binFile.exists() && binFile.mkdirs()) { jp.setOutputLocation( binPath, monitor ); } Set<IClasspathEntry> entries = new HashSet<IClasspathEntry>(); entries.addAll( Arrays.asList( jp.getRawClasspath())); entries.add( JavaRuntime.getDefaultJREContainerEntry()); // // Remove the "src" entry IClasspathEntry srcEntry = null; for( IClasspathEntry entry : entries ) { if( entry.getEntryKind() == IClasspathEntry.CPE_SOURCE ) { srcEntry = entry; break; } } // // Specify a new source entry if( srcEntry != null ) entries.remove( srcEntry ); String[] srcPaths = new String[] { PetalsConstants.LOC_SRC_FOLDER, PetalsConstants.LOC_JAVA_RES_FOLDER }; for( String s : srcPaths ) { IPath srcPath = ppath.append( s ); File srcFile = ResourcesPlugin.getWorkspace().getRoot().getLocation().append( srcPath ).toFile(); if( srcFile.exists() && srcFile.isDirectory() || ! srcFile.exists() && srcFile.mkdirs()) { project.refreshLocal( IResource.DEPTH_INFINITE, monitor ); srcEntry = JavaCore.newSourceEntry( srcPath ); entries.add( srcEntry ); } else { PetalsCommonPlugin.log( "Could not set '" + s + "' as a source folder.", IStatus.ERROR ); } } jp.setRawClasspath( entries.toArray( new IClasspathEntry[ entries.size()]), monitor ); } return jp; } /** * Gets the source folders of a IJavaProject. * @param javaProject * @return the list of source folders in this Java project */ public static List<IClasspathEntry> getSourceFolders( IJavaProject javaProject ) { List<IClasspathEntry> result = new ArrayList<IClasspathEntry>(); try { for( IClasspathEntry entry : javaProject.getRawClasspath()) { if( entry.getEntryKind() == IClasspathEntry.CPE_SOURCE ) result.add( entry ); } } catch( JavaModelException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } return result; } /** * Get the class path from Java project. * * @param javaProject * @param getReferencedProjectClasspath * @param binaryDirectory * @return the class path as a list of string locations. */ public static List<String> getClasspath( IJavaProject javaProject, boolean getReferencedProjectClasspath, boolean binaryDirectory ) { List<String> paths = new ArrayList<String>(); try { if( javaProject != null ) { // Get the raw class path IClasspathEntry[] entries = javaProject.getRawClasspath(); for( IClasspathEntry entry : entries ) { switch( entry.getEntryKind()) { case IClasspathEntry.CPE_PROJECT: if( ! getReferencedProjectClasspath ) break; String projectName = entry.getPath().toString(); IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject( projectName ); IJavaProject jProject = JavaCore.create( project ); List<String> subPaths = getClasspath( jProject, true, binaryDirectory ); paths.addAll( subPaths ); break; case IClasspathEntry.CPE_LIBRARY: IPath path = entry.getPath(); paths.add( path.toString()); break; case IClasspathEntry.CPE_VARIABLE: entry = JavaCore.getResolvedClasspathEntry( entry ); if( entry != null ) { path = entry.getPath(); paths.add( path.toString()); } break; } } // Add the "bin" directory? if( binaryDirectory && javaProject.getOutputLocation() != null ) { IPath path = ResourcesPlugin.getWorkspace().getRoot().getLocation(); path = path.append( javaProject.getOutputLocation()); paths.add( path.toString()); } } } catch( JavaModelException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } return paths; } /** * Creates a JAR file containing all the resources contained in the source folders of a Java project. * @param jp the Java project * @param monitor the progress monitor * @return the JAR file, which was saved in the temporary folder (and should be deleted after use) * @throws InvocationTargetException if the creation failed * @throws InterruptedException if the creation was interrupted */ public static File createDefaultJar( IJavaProject jp, IProgressMonitor monitor ) throws InvocationTargetException, InterruptedException { // Create a default JAR for the implementation JarPackageData jarDescription = new JarPackageData(); jarDescription.setIncludeDirectoryEntries( true ); jarDescription.setExportClassFiles( true ); jarDescription.setOverwrite( true ); jarDescription.setIncludeDirectoryEntries( true ); jarDescription.setExportJavaFiles( false ); jarDescription.setCompress( true ); jarDescription.setExportErrors( true ); jarDescription.setExportWarnings( true ); // Add all the files in the source folders List<Object> filesToPackage = new ArrayList<Object> (); for( IClasspathEntry entry : getSourceFolders( jp )) { // PETALSSTUD-130: use the project location and not the workspace root as a reference File f = jp.getProject().getLocation().append( entry.getPath().removeFirstSegments( 1 )).toFile(); // PETALSSTUD-130 IFolder folder = (IFolder) ResourceUtils.getResource( f ); if( folder != null ) { List<IFile> files = ResourceUtils.getFilesByRegexp( folder, ".*" ); filesToPackage.addAll( files ); } } Object[] elements = new Object[ filesToPackage.size()]; jarDescription.setElements( filesToPackage.toArray( elements )); // Create the JAR IPath jarLocation = new Path( System.getProperty( "java.io.tmpdir" )).append( jp.getProject().getName() + ".jar" ); // Bug: Windows => path separator = "\" // But IPath#isAbsolute() only checks for "/" path separators // => We replace the path separator // Otherwise, the JAR is created, but not at the location we would expect jarLocation = new Path( jarLocation.toString().replaceAll( "\\\\", "/" )); jarDescription.setJarLocation( jarLocation ); IJarExportRunnable runnable = jarDescription.createJarExportRunnable( null ); runnable.run( monitor ); IStatus status = runnable.getStatus(); if( ! status.isOK()) PetalsCommonPlugin.getDefault().getLog().log( status ); return jarLocation.toFile(); } /** * Creates a new source folder. * <p> * If the folder does not exist, it is created.<br /> * If the directory is already in the class path, then this method does nothing. * </p> * * @param jp the Java project * @param folderName the folder name * @param monitor the progress monitor * @return the created folder * @throws CoreException if something went wrong */ public static IFolder createSourceFolder( IJavaProject jp, String folderName, IProgressMonitor monitor ) throws CoreException { IFolder newSourceFolder = jp.getProject().getFolder( folderName ); if( ! newSourceFolder.exists()) newSourceFolder.create( true, true, monitor ); IClasspathEntry srcEntry = JavaCore.newSourceEntry( newSourceFolder.getFullPath()); Set<IClasspathEntry> entries = new HashSet<IClasspathEntry>(); entries.addAll( Arrays.asList( jp.getRawClasspath())); entries.add( srcEntry ); jp.setRawClasspath( entries.toArray( new IClasspathEntry[ entries.size()]), monitor ); return newSourceFolder; } /** * Gets a set of JAR files from a Java project, in order to package them. * @param jp the Java project * @param monitor the progress monitor * @return an instance of {@link ExportableClassPath} (never null) */ public static ExportableClassPath getExportableClasspath( IJavaProject jp, IProgressMonitor monitor ) { ExportableClassPath result = new ExportableClassPath(); try { // Check the class path IClasspathEntry[] entries = jp.getRawClasspath(); for( IClasspathEntry entry : entries ) updateExportableResult( result, entry, monitor ); // Package the implementation File jarFile = createDefaultJar( jp, monitor ); result.implementationJars.add( jarFile ); monitor.worked( 1 ); } catch( JavaModelException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } catch( InvocationTargetException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } catch( InterruptedException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } return result; } /** * Populates an {@link ExportableClassPath} instance from a class path entry. * @param result the {@link ExportableClassPath} instance to populate * @param entry a class path entry * @param monitor the progress monitor */ private static void updateExportableResult( ExportableClassPath result, IClasspathEntry entry, IProgressMonitor monitor ) { switch( entry.getEntryKind()) { case IClasspathEntry.CPE_PROJECT: String projectName = entry.getPath().toString(); IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject( projectName ); IJavaProject jProject = JavaCore.create( project ); ExportableClassPath subResult = getExportableClasspath( jProject, monitor ); result.implementationJars.addAll( subResult.implementationJars ); result.libraryJars.addAll( subResult.libraryJars ); monitor.worked( 1 ); break; case IClasspathEntry.CPE_LIBRARY: IPath path = entry.getPath(); if( path != null ) { File f = path.toFile(); if( f.exists()) { if( f.getName().endsWith( ".zip" ) || f.getName().endsWith( ".jar" )) result.libraryJars.add( f ); } } break; case IClasspathEntry.CPE_VARIABLE: entry = JavaCore.getResolvedClasspathEntry( entry ); if( entry != null ) updateExportableResult( result, entry, monitor ); break; } } /** * This class is used to manage the class path elements to export for a Java project. * <p> * It manages through 2 distinct lists the libraries used in the project, and the * implementations JAR that are built during the export operation. * </p> * <p> * Once this class has been used, the method {@link #deleteImplementationJars()} should * be invoked. * </p> */ public static class ExportableClassPath { private final Set<File> libraryJars = new HashSet<File> (); private final Set<File> implementationJars = new HashSet<File> (); /** * @return the class path, including both the library and the */ public Set<File> getExportableClassPath() { Set<File> result = new HashSet<File> (); result.addAll( this.libraryJars ); result.addAll( this.implementationJars ); return result; } /** * Deletes the all (temporary) implementation JAR files. */ public void deleteImplementationJars() { for( File f : this.implementationJars ) { if( ! f.exists()) PetalsCommonPlugin.log( f.getAbsolutePath() + " does not exist.", IStatus.WARNING ); if( f.exists() && ! f.delete()) f.deleteOnExit(); } } } /** * Finds the direct children for a given package. * <p> * Copied (or almost) from the JDT. * </p> * * @param parent the package fragment root (will be searched from <code>fragment</code> if null) * @param fragment the fragment to analyze (can be null if <code>parent</code> is not) * @return a list of package fragments * @throws JavaModelException if an error occurred with the Java model */ public static List<IPackageFragment> findDirectSubPackages( IPackageFragmentRoot parent, IPackageFragment fragment ) throws JavaModelException { // Special case for the default package if( fragment != null && fragment.isDefaultPackage()) return Collections.emptyList(); // Find the package parent? if( parent == null ) { IJavaElement elt = fragment; while( elt.getParent() != null ) { elt = elt.getParent(); if( elt instanceof IPackageFragmentRoot ) { parent = (IPackageFragmentRoot) elt; break; } } } // Find the direct children List<IPackageFragment> result = new ArrayList<IPackageFragment> (); if( parent != null ) { IJavaElement[] children = parent.getChildren(); String prefix = fragment != null ? fragment.getElementName() + '.' : ""; //$NON-NLS-1$ int prefixLen = prefix.length(); for( IJavaElement element : children ) { IPackageFragment curr = (IPackageFragment) element; String name = curr.getElementName(); if( name.startsWith( prefix ) && name.length() > prefixLen && name.indexOf( '.', prefixLen ) == -1 ) result.add( curr ); else if( fragment == null && curr.isDefaultPackage()) result.add( curr ); } } return result; } }