/******************************************************************************* * Copyright (c) 2009, 2010 SpringSource, a divison of VMware, 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: * SpringSource, a division of VMware, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.virgo.ide.bundlor.internal.core; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.Manifest; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; 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.core.runtime.Status; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; 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.osgi.util.ManifestElement; import org.eclipse.pde.internal.core.text.bundle.ManifestHeader; import org.eclipse.ui.statushandlers.StatusManager; import org.eclipse.virgo.bundlor.ClassPath; import org.eclipse.virgo.bundlor.ClassPathEntry; import org.eclipse.virgo.bundlor.EntryScannerListener; import org.eclipse.virgo.bundlor.ManifestGenerator; import org.eclipse.virgo.bundlor.support.ArtifactAnalyzer; import org.eclipse.virgo.bundlor.support.classpath.StandardClassPathFactory; import org.eclipse.virgo.bundlor.support.partialmanifest.PartialManifest; import org.eclipse.virgo.bundlor.support.partialmanifest.ReadablePartialManifest; import org.eclipse.virgo.bundlor.support.properties.FileSystemPropertiesSource; import org.eclipse.virgo.bundlor.support.properties.PropertiesSource; import org.eclipse.virgo.bundlor.util.SimpleManifestContents; import org.eclipse.virgo.ide.bundlor.internal.core.asm.ExtensibleAsmTypeArtefactAnalyser; import org.eclipse.virgo.ide.bundlor.jdt.core.AstTypeArtifactAnalyser; import org.eclipse.virgo.ide.facet.core.FacetCorePlugin; import org.eclipse.virgo.ide.facet.core.FacetUtils; import org.eclipse.virgo.ide.manifest.core.BundleManifestUtils; import org.eclipse.virgo.ide.manifest.core.editor.model.SpringBundleModel; import org.eclipse.virgo.ide.manifest.core.editor.model.SpringBundleModelFactory; import org.eclipse.virgo.ide.module.core.ServerModuleDelegate; import org.eclipse.virgo.util.osgi.manifest.BundleManifest; import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory; import org.eclipse.virgo.util.osgi.manifest.ExportedPackage; import org.eclipse.virgo.util.osgi.manifest.ImportedPackage; import org.eclipse.virgo.util.parser.manifest.ManifestContents; import org.eclipse.virgo.util.parser.manifest.ManifestParser; import org.eclipse.virgo.util.parser.manifest.RecoveringManifestParser; import org.osgi.framework.BundleException; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.ide.eclipse.core.SpringCorePreferences; import org.springframework.ide.eclipse.core.SpringCoreUtils; import org.springframework.ide.eclipse.core.internal.project.SpringProjectContributionManager.ResourceDeltaVisitor; import org.springframework.ide.eclipse.core.internal.project.SpringProjectContributionManager.ResourceTreeVisitor; import org.springframework.ide.eclipse.core.project.IProjectContributor; import org.springframework.util.StringUtils; /** * {@link IncrementalProjectBuilder} that runs on a bundle project and generates the MANIFEST.MF based on actual * dependencies from the source code and the Bundlor template. * @author Christian Dupuis * @author Leo Dos Santos * @since 1.1.2 */ @SuppressWarnings("restriction") public class BundlorProjectBuilder extends IncrementalProjectBuilder { private final static String CLASS_FILE_EXTENSION = ".class"; /** Deleted sources from a source directory */ private Set<IResource> deletedSourceResources = new HashSet<IResource>(); /** Deleted sources from a test source directory */ private Set<IResource> deletedTestResources = new HashSet<IResource>(); /** Change or new sources from a source directory */ private Set<IResource> sourceResources = new HashSet<IResource>(); /** Change or new sources from a test source directory */ private Set<IResource> testResources = new HashSet<IResource>(); /** WEB-INF/web.xml resource */ private IResource webXmlResource = null; /** <code>true</code> if a MANIFEST.MF, TEST.MF or template.mf has been changed */ private boolean templateChanged = false; /** <code>true</code> if Bundlor should scan byte code instead of source code */ private boolean scanByteCode = true; private List<ImportedPackage> templatePackageImports = new ArrayList<ImportedPackage>(); /** * {@inheritDoc} */ @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { // Clean out old state deletedSourceResources.clear(); sourceResources.clear(); deletedTestResources.clear(); testResources.clear(); templatePackageImports.clear(); templateChanged = false; webXmlResource = null; IProject project = getProject(); IResourceDelta delta = getDelta(project); // Get the configuration setting scanByteCode = SpringCorePreferences.getProjectPreferences(project, BundlorCorePlugin.PLUGIN_ID).getBoolean( BundlorCorePlugin.TEMPLATE_BYTE_CODE_SCANNING_KEY, BundlorCorePlugin.TEMPLATE_BYTE_CODE_SCANNING_DEFAULT); // Prepare the list of changed resources visitResourceDelta(project, kind, delta); // Trigger build build(kind, monitor); // Refresh PDE manifest in the root of the project IFile file = project.getFile("META-INF/MANIFEST.MF"); if (file != null && file.exists()) { file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor()); } return null; } /** * Checks if the given {@link IResource} is either on source or test source folders. */ private void addResourceIfInSourceFolder(IResource resource, Set<IClasspathEntry> classpathEntries, Set<IClasspathEntry> testClasspathEntries) { for (IClasspathEntry entry : classpathEntries) { if (entry.getPath().isPrefixOf(resource.getFullPath())) { sourceResources.add(resource); return; } } for (IClasspathEntry entry : testClasspathEntries) { if (entry.getPath().isPrefixOf(resource.getFullPath())) { testResources.add(resource); return; } } } private void build(int kind, final IProgressMonitor monitor) throws CoreException { monitor.beginTask("Scanning source code to generate MANIFEST.MF", sourceResources.size() + testResources.size()); // No resources selected -> on relevant change if (sourceResources.size() == 0 && testResources.size() == 0 && deletedSourceResources.size() == 0 && deletedTestResources.size() == 0 && !templateChanged) { return; } // Increase scope of build to FULL_BUILD if template.mf, MANIFEST.MF or TEST.MF has changed if (templateChanged) { kind = IncrementalProjectBuilder.FULL_BUILD; } IncrementalPartialManifestManager manifestManager = BundlorCorePlugin.getDefault().getManifestManager(); IJavaProject javaProject = JavaCore.create(getProject()); // No incremental manifest model has been recorded or the build is a full build final boolean isFullBuild = !manifestManager.hasPartialManifest(javaProject) || kind == IncrementalProjectBuilder.FULL_BUILD; final ReadablePartialManifest model = manifestManager.getPartialManifest(javaProject, false, isFullBuild); final ReadablePartialManifest testModel = manifestManager.getPartialManifest(javaProject, true, isFullBuild); Set<PropertiesSource> propertiesSources = createPropertiesSource(javaProject); // Firstly create the MANFIEST.MF ArtifactAnalyzer artefactAnalyser = (scanByteCode ? new ProgressReportingAsmTypeArtefactAnalyser(monitor) : new ProgressReportingAstTypeArtefactAnalyser(javaProject, monitor)); BundleManifest manifest = generateManifest( javaProject, model, ManifestGeneratorFactory.create(model, artefactAnalyser, propertiesSources.toArray(new PropertiesSource[propertiesSources.size()])), sourceResources, isFullBuild, false); // Secondly create the TEST.MF BundleManifest testManifest = generateManifest( javaProject, testModel, ManifestGeneratorFactory.create(testModel, artefactAnalyser, propertiesSources.toArray(new PropertiesSource[propertiesSources.size()])), testResources, isFullBuild, true); // Lastly merge the manifests mergeManifests(javaProject, manifest, testManifest); monitor.done(); } /** * Set up {@link PropertiesSource} instances for configured properties files. */ private Set<PropertiesSource> createPropertiesSource(final IJavaProject javaProject) throws CoreException { Set<PropertiesSource> propertiesSources = new HashSet<PropertiesSource>(); SpringCorePreferences preferences = SpringCorePreferences.getProjectPreferences(javaProject.getProject(), BundlorCorePlugin.PLUGIN_ID); String propertiesFiles = preferences.getString(BundlorCorePlugin.TEMPLATE_PROPERTIES_FILE_KEY, BundlorCorePlugin.TEMPLATE_PROPERTIES_FILE_DEFAULT); String[] properties = StringUtils.delimitedListToStringArray(propertiesFiles, ";"); List<Resource> resources = new ArrayList<Resource>(); if (properties != null && properties.length > 0) { for (String propertiesFile : properties) { Resource resource = null; try { // Assume file is relative to the project but still in the workspace IFile propertiesResource = javaProject.getProject().getFile(propertiesFile); if (propertiesResource.exists()) { if (propertiesResource.isLinked()) { URI uri = propertiesResource.getLocationURI(); if (uri != null) { resource = new FileSystemResource(new File(uri)); } } else { URI uri = propertiesResource.getRawLocationURI(); if (uri != null) { resource = new FileSystemResource(new File(uri)); } } } } catch (RuntimeException e) { // Ignore; simply means the file is not relative to the project } // Assume file is relative to the project and can be outside of the project and workspace if (resource == null) { URI uri = javaProject.getProject().getLocationURI(); if (uri != null) { File source = new File(new File(uri), propertiesFile); if (source.exists()) { resource = new FileSystemResource(source); } } uri = javaProject.getProject().getRawLocationURI(); if (uri != null) { File source = new File(new File(uri), propertiesFile); if (source.exists()) { resource = new FileSystemResource(source); } } } if (resource == null) { // Assume file is relative to the workspace try { IFile propertiesResource = javaProject.getProject().getWorkspace().getRoot() .getFile(new Path(propertiesFile)); if (propertiesResource.exists()) { URI uri = propertiesResource.getRawLocationURI(); if (uri != null) { resources.add(new FileSystemResource(new File(uri))); } } } catch (RuntimeException e) { // Ignore; simply means the file is not relative to the workspace } } if (resource == null) { // Assume file is a full qualified in the file system File nativeFile = new File(propertiesFile); if (nativeFile.exists()) { resource = new FileSystemResource(nativeFile); } else { // Assume relative to the project if (javaProject.getProject().getRawLocation() != null) { File parent = javaProject.getProject().getRawLocation().toFile(); if (parent.exists()) { File file = new File(parent, propertiesFile); if (file.exists()) { resources.add(new FileSystemResource(file)); } } } else if (javaProject.getProject().getLocation() != null) { File parent = javaProject.getProject().getLocation().toFile(); if (parent.exists()) { File file = new File(parent, propertiesFile); if (file.exists()) { resources.add(new FileSystemResource(file)); } } } } } if (resource != null) { resources.add(resource); } // TODO CD we could add an error marker for those files that can't be resolved to a resource } } // Add in properties sources for the resolved properties files for (Resource resource : resources) { try { propertiesSources.add(new FileSystemPropertiesSource(resource.getFile())); } catch (IOException e) { } } return propertiesSources; } /** * Create a {@link Manifest} instance from the file at the given path. */ private ManifestContents createManifestFromPath(IResource templateResource) { if (templateResource != null) { ManifestParser parser = new RecoveringManifestParser(); ManifestContents manifest = null; try { manifest = parser.parse(new FileReader(templateResource.getRawLocation().toString())); } catch (IOException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, BundlorCorePlugin.PLUGIN_ID, "An IO Exception occurred.", e)); } return manifest; } // Create new empty MANIFEST else { return new SimpleManifestContents(); } } private void doGetAffectedResources(IResource resource, int kind, int deltaKind) throws CoreException { IJavaProject project = JavaCore.create(getProject()); if (project == null) { return; } IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); // Get the source folders Set<IClasspathEntry> classpathEntries = ServerModuleDelegate.getSourceClasspathEntries(resource.getProject(), false); Set<IClasspathEntry> testClasspathEntries = ServerModuleDelegate.getSourceClasspathEntries( resource.getProject(), true); // Java source files if (!scanByteCode && resource.getName().endsWith("java")) { //$NON-NLS-1$ IJavaElement element = JavaCore.create(resource); if (element != null && element.getJavaProject().isOnClasspath(element)) { IPackageFragmentRoot root = (IPackageFragmentRoot) element .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); try { IClasspathEntry classpathEntry = root.getRawClasspathEntry(); for (IClasspathEntry entry : classpathEntries) { if (classpathEntry.equals(entry)) { if (deltaKind == IResourceDelta.REMOVED) { deletedSourceResources.add(resource); } else { sourceResources.add(resource); } break; } } for (IClasspathEntry entry : testClasspathEntries) { if (classpathEntry.equals(entry)) { if (deltaKind == IResourceDelta.REMOVED) { deletedTestResources.add(resource); } else { testResources.add(resource); } break; } } } catch (JavaModelException e) { // This can happen in case of .java resources not on the classpath of the project } } } // Java byte code else if (scanByteCode && resource.getName().endsWith(CLASS_FILE_EXTENSION)) { IPath classFilePath = resource.getFullPath(); // Check default output folders IPath defaultOutputLocation = project.getOutputLocation(); if (defaultOutputLocation.isPrefixOf(classFilePath)) { // Ok we know that the file is a class in the default output location; let's get the class name String className = classFilePath.removeFirstSegments(defaultOutputLocation.segmentCount()).toString(); className = className.substring(0, className.length() - CLASS_FILE_EXTENSION.length()); int ix = className.indexOf('$'); if (ix > 0) { className = className.substring(0, ix); } className = className + ".java"; if (deltaKind == IResourceDelta.REMOVED) { deletedSourceResources.add(resource); deletedTestResources.add(resource); } else { for (IClasspathEntry entry : classpathEntries) { IPath sourceLocation = entry.getPath(); IResource sourceFolder = wsRoot.findMember(sourceLocation); if (sourceFolder instanceof IFolder) { if (((IFolder) sourceFolder).findMember(className) != null) { sourceResources.add(resource); break; } } } for (IClasspathEntry entry : testClasspathEntries) { IPath sourceLocation = entry.getPath(); IResource sourceFolder = wsRoot.findMember(sourceLocation); if (sourceFolder instanceof IFolder) { if (((IFolder) sourceFolder).findMember(className) != null) { testResources.add(resource); break; } } } } } // Check output folders of source folders for (IClasspathEntry entry : classpathEntries) { IPath outputLocation = entry.getOutputLocation(); if (outputLocation != null && outputLocation.isPrefixOf(classFilePath)) { if (deltaKind == IResourceDelta.REMOVED) { deletedSourceResources.add(resource); } else { sourceResources.add(resource); } break; } } // Check output folders for test source folders for (IClasspathEntry entry : testClasspathEntries) { IPath outputLocation = entry.getOutputLocation(); if (outputLocation != null && outputLocation.isPrefixOf(classFilePath)) { if (deltaKind == IResourceDelta.REMOVED) { deletedTestResources.add(resource); } else { testResources.add(resource); } break; } } } // Bundlor template has changed else if (resource.getName().equals("template.mf") || SpringCoreUtils.isManifest(resource)) { templateChanged = true; } // Hibernate mapping files else if (resource.getName().endsWith(".hbm")) { addResourceIfInSourceFolder(resource, classpathEntries, testClasspathEntries); } // JPA persistence descriptor else if (resource.getName().equals("persistence.xml") && resource.getParent() != null && resource.getParent().getName().equals("META-INF")) { addResourceIfInSourceFolder(resource, classpathEntries, testClasspathEntries); } else if (resource.getFullPath().toString().endsWith("WEB-INF/web.xml") && FacetUtils.hasProjectFacet(resource, FacetCorePlugin.WEB_FACET_ID)) { IResource webResource = getWebXmlResource(); if (resource.equals(webResource)) { sourceResources.add(resource); } } // Spring configuration file else if (resource.getName().endsWith(".xml")) { addResourceIfInSourceFolder(resource, classpathEntries, testClasspathEntries); } } /** * Generate a new or update the existing manifest. */ private BundleManifest generateManifest(IJavaProject javaProject, ReadablePartialManifest model, ManifestGenerator generator, Set<IResource> resources, boolean isFullBuild, boolean isTestManifest) throws JavaModelException, CoreException { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); Set<String> folders = getSourceFolders(javaProject, root, isTestManifest); // Removed deleted resources for (IResource deletedResource : (isTestManifest ? deletedTestResources : deletedSourceResources)) { String path = deletedResource.getRawLocation().toString(); for (String folder : folders) { if (path.startsWith(folder)) { path = path.substring(folder.length() + 1); if (model instanceof EntryScannerListener) ((EntryScannerListener) model).onBeginEntry(path); ((EntryScannerListener) model).onEndEntry(); } } } IResource templateResource = getProject().findMember("template.mf"); ManifestContents templateManifest = createManifestFromPath(templateResource); // Test-Import-Package and Test-Import-Bundle -> Import-Package and Import-Bundle if (isTestManifest) { templateManifest.getMainAttributes().remove("Import-Package"); templateManifest.getMainAttributes().remove("Import-Bundle"); templateManifest.getMainAttributes().remove("Import-Library"); if (templateManifest.getMainAttributes().containsKey("Test-Import-Bundle")) { templateManifest.getMainAttributes().put("Import-Bundle", templateManifest.getMainAttributes().get("Test-Import-Bundle")); } if (templateManifest.getMainAttributes().containsKey("Test-Import-Package")) { templateManifest.getMainAttributes().put("Import-Package", templateManifest.getMainAttributes().get("Test-Import-Package")); } if (templateManifest.getMainAttributes().containsKey("Test-Import-Library")) { templateManifest.getMainAttributes().put("Import-Library", templateManifest.getMainAttributes().get("Test-Import-Library")); } } else { String importPackageHeader = templateManifest.getMainAttributes().get("Import-Package"); Dictionary<String, String> contents = new Hashtable<String, String>(); if (importPackageHeader != null) { contents.put("Import-Package", importPackageHeader); } templatePackageImports.addAll(BundleManifestFactory.createBundleManifest(contents).getImportPackage() .getImportedPackages()); } ManifestContents manifest = null; List<ClassPath> classpathEntries = new ArrayList<ClassPath>(); StandardClassPathFactory factory = new StandardClassPathFactory(); if (isFullBuild) { for (String folder : folders) { classpathEntries.add(factory.create(folder)); } } else { for (String folder : folders) { classpathEntries.add(new FilteringClassPath(resources, folder)); } } manifest = generator.generate(templateManifest, classpathEntries.toArray(new ClassPath[classpathEntries.size()])); return org.eclipse.virgo.bundlor.util.BundleManifestUtils.createBundleManifest(manifest); } /** * Returns the source folders of the given {@link IJavaProject}. */ private Set<String> getSourceFolders(IJavaProject javaProject, IWorkspaceRoot root, boolean testFolders) throws JavaModelException { Set<String> folders = new HashSet<String>(); for (IClasspathEntry entry : ServerModuleDelegate.getSourceClasspathEntries(getProject(), testFolders)) { IResource sourceFolder = root.findMember(entry.getPath()); if (sourceFolder instanceof IFolder && !(sourceFolder instanceof IWorkspaceRoot)) { folders.add(((IFolder) sourceFolder).getRawLocation().toString()); } if (scanByteCode && entry.getOutputLocation() != null) { IResource classFolder = root.findMember(entry.getOutputLocation()); if (classFolder instanceof IFolder && !(classFolder instanceof IWorkspaceRoot)) { folders.add(((IFolder) classFolder).getRawLocation().toString()); } } } if (scanByteCode) { IResource sourceFolder = root.findMember(javaProject.getOutputLocation()); if (sourceFolder instanceof IFolder && !(sourceFolder instanceof IWorkspaceRoot)) { folders.add(((IFolder) sourceFolder).getRawLocation().toString()); } } // Add parent folder of web.xml if (!testFolders && FacetUtils.hasProjectFacet(getProject(), FacetCorePlugin.WEB_FACET_ID)) { IResource resource = getWebXmlResource(); if (resource != null) { folders.add(resource.getRawLocation().removeLastSegments(2).toString()); } } return folders; } /** * Merges the manifests. */ private void mergeManifests(final IJavaProject javaProject, BundleManifest manifest, BundleManifest testManifest) throws CoreException { // Check if there is a manifest if (manifest == null) { return; } BundleManifest cleanTestManifest = null; if (testManifest != null) { /* * As the test manifest should not contain imported packages, bundles and libraries those need to be * removed. Furthermore there shouldn't be any bundlor related headers in the manifest. */ cleanTestManifest = BundleManifestFactory.createBundleManifest(); cleanTestManifest.setBundleManifestVersion(2); if (testManifest.getImportBundle() != null && testManifest.getImportBundle().getImportedBundles() != null && testManifest.getImportBundle().getImportedBundles().size() > 0) { cleanTestManifest.getImportBundle().getImportedBundles() .addAll(testManifest.getImportBundle().getImportedBundles()); } if (testManifest.getImportLibrary() != null && testManifest.getImportLibrary().getImportedLibraries() != null && testManifest.getImportLibrary().getImportedLibraries().size() > 0) { cleanTestManifest.getImportLibrary().getImportedLibraries() .addAll(testManifest.getImportLibrary().getImportedLibraries()); } for (ImportedPackage packageImport : testManifest.getImportPackage().getImportedPackages()) { boolean notImported = true; for (ImportedPackage manifestPackageImport : manifest.getImportPackage().getImportedPackages()) { if (manifestPackageImport.getPackageName().equals(packageImport.getPackageName())) { notImported = false; break; } } if (notImported) { boolean skip = false; for (ExportedPackage packageExport : manifest.getExportPackage().getExportedPackages()) { String packageImportName = packageImport.getPackageName(); if (packageExport.getPackageName().equals(packageImportName)) { skip = true; } } if (!skip) { cleanTestManifest.getImportPackage().getImportedPackages().add(packageImport); } } } } /* * Never ever import packages that are exported already; we need this as the STS integrated support will * sometimes try to import certain packages that are usually exported already if the classpath is incomplete. */ List<ImportedPackage> importedPackagesCopy = new ArrayList<ImportedPackage>(manifest.getImportPackage() .getImportedPackages()); for (ExportedPackage packageExport : manifest.getExportPackage().getExportedPackages()) { for (ImportedPackage packageImport : importedPackagesCopy) { if (packageExport.getPackageName().equals(packageImport.getPackageName())) { boolean remove = true; // It might still be that the user explicitly wants the package import in the template.mf for (ImportedPackage templatePackageImport : templatePackageImports) { if (packageExport.getPackageName().equals(templatePackageImport.getPackageName())) { remove = false; break; } } if (remove) { manifest.getImportPackage().getImportedPackages().remove(packageImport); break; } } } } IResource manifestResource = BundleManifestUtils.locateManifest(javaProject, false); if (manifestResource == null) { manifestResource = BundleManifestUtils.getFirstPossibleManifestFile(getProject(), false); } IResource testManifestResource = BundleManifestUtils.locateManifest(javaProject, true); if (testManifestResource == null) { testManifestResource = BundleManifestUtils.getFirstPossibleManifestFile(getProject(), true); } boolean formatPref = SpringCorePreferences.getProjectPreferences(javaProject.getProject(), BundlorCorePlugin.PLUGIN_ID).getBoolean(BundlorCorePlugin.FORMAT_GENERATED_MANIFESTS_KEY, BundlorCorePlugin.FORMAT_GENERATED_MANIFESTS_DEFAULT); if (manifestResource != null && manifestResource instanceof IFile) { try { StringWriter writer = new StringWriter(); manifest.write(writer); InputStream manifestStream = new ByteArrayInputStream(writer.toString().getBytes()); if (formatPref) { manifestStream = formatManifest((IFile) manifestResource, manifestStream); } if (SpringCoreUtils.validateEdit((IFile) manifestResource)) { ((IFile) manifestResource).setContents(manifestStream, IResource.FORCE | IResource.KEEP_HISTORY, new NullProgressMonitor()); } writer.close(); manifestStream.close(); } catch (IOException e) { } } if (testManifestResource != null && testManifestResource instanceof IFile) { try { StringWriter writer = new StringWriter(); cleanTestManifest.write(writer); InputStream testManifestStream = new ByteArrayInputStream(writer.toString().getBytes()); if (formatPref) { testManifestStream = formatManifest((IFile) testManifestResource, testManifestStream); } if (SpringCoreUtils.validateEdit((IFile) testManifestResource)) { ((IFile) testManifestResource).setContents(testManifestStream, IResource.FORCE | IResource.KEEP_HISTORY, new NullProgressMonitor()); } writer.close(); testManifestStream.close(); } catch (IOException e) { } } } @SuppressWarnings({ "unchecked" }) private InputStream formatManifest(IFile file, InputStream manifestInput) throws IOException { StringWriter writer = new StringWriter(); SpringBundleModel model = new SpringBundleModel("", true); SpringBundleModelFactory factory = new SpringBundleModelFactory(model); try { Map headers = ManifestElement.parseBundleManifest(manifestInput, null); for (Object obj : headers.keySet()) { String key = (String) obj; String value = (String) headers.get(key); ManifestHeader header = (ManifestHeader) factory.createHeader(key, value); header.update(false); String result = header.write(); writer.write(result); } } catch (BundleException e) { } String manifestOutput = writer.toString(); writer.close(); manifestInput.close(); model.dispose(); return new ByteArrayInputStream(manifestOutput.getBytes()); } /** * Visit the {@link IResourceDelta} and prepare the internal structures of changed and removed resources. */ private void visitResourceDelta(IProject project, int kind, IResourceDelta delta) throws CoreException { if (delta == null || kind == IncrementalProjectBuilder.FULL_BUILD) { ResourceTreeVisitor visitor = new ResourceTreeVisitor(new IProjectContributor() { public void cleanup(IResource resource, IProgressMonitor monitor) throws CoreException { } public Set<IResource> getAffectedResources(IResource resource, int kind, int deltaKind) throws CoreException { doGetAffectedResources(resource, kind, deltaKind); return Collections.emptySet(); } }); project.accept(visitor); } else { ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(new IProjectContributor() { public void cleanup(IResource resource, IProgressMonitor monitor) throws CoreException { } public Set<IResource> getAffectedResources(IResource resource, int kind, int deltaKind) throws CoreException { doGetAffectedResources(resource, kind, deltaKind); return Collections.emptySet(); } }, kind); delta.accept(visitor); } } private IResource getWebXmlResource() { if (webXmlResource == null) { webXmlResource = SpringCoreUtils.getDeploymentDescriptor(getProject()); } return webXmlResource; } /** * Extension to {@link AstTypeArtifactAnalyser} that takes a {@link IProgressMonitor} to report monitor */ class ProgressReportingAstTypeArtefactAnalyser extends AstTypeArtifactAnalyser { private final IProgressMonitor monitor; public ProgressReportingAstTypeArtefactAnalyser(IJavaProject javaProject, IProgressMonitor monitor) { super(javaProject); this.monitor = monitor; } @Override public void analyse(InputStream is, String name, PartialManifest model) throws Exception { monitor.subTask("Scanning '" + name + "'"); super.analyse(is, name, model); monitor.worked(1); } } /** * @author Christian Dupuis * @since 2.1.1 */ class ProgressReportingAsmTypeArtefactAnalyser extends ExtensibleAsmTypeArtefactAnalyser { private final IProgressMonitor monitor; public ProgressReportingAsmTypeArtefactAnalyser(IProgressMonitor monitor) { this.monitor = monitor; } @Override public void analyse(InputStream is, String name, PartialManifest model) throws Exception { monitor.subTask("Scanning '" + name + "'"); super.analyse(is, name, model); monitor.worked(1); } } class FilteringClassPath implements ClassPath { private final Map<String, ClassPathEntry> entries; public FilteringClassPath(Set<IResource> resources, String folder) { this.entries = new HashMap<String, ClassPathEntry>(); for (IResource resource : resources) { if (resource instanceof IFile) { String path = resource.getRawLocation().toString(); if (path.startsWith(folder)) { path = path.substring(folder.length() + 1); entries.put(path, new FileClassPathEntry(path, (IFile) resource)); } } } } public void close() { // Nothing to close } public ClassPathEntry getEntry(String name) { return entries.get(name); } public Iterator<ClassPathEntry> iterator() { return entries.values().iterator(); } } class FileClassPathEntry implements ClassPathEntry { private final String name; private final IFile file; public FileClassPathEntry(String name, IFile file) { this.name = name; this.file = file; } public InputStream getInputStream() { try { return file.getContents(true); } catch (CoreException e) { throw new RuntimeException(e); } } public String getName() { return name; } public Reader getReader() { return new InputStreamReader(getInputStream()); } public boolean isDirectory() { return false; } } }