org.bndtools.builder.classpath.BndContainerSourceManager Maven / Gradle / Ivy
The newest version!
package org.bndtools.builder.classpath;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import org.bndtools.builder.BndtoolsBuilder;
import org.bndtools.builder.BuilderPlugin;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
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.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaCore;
import aQute.bnd.build.WorkspaceRepository;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Domain;
import aQute.bnd.service.RepositoryPlugin;
import aQute.bnd.version.Version;
import aQute.lib.io.IO;
import bndtools.central.Central;
import bndtools.central.RepositoryUtils;
public class BndContainerSourceManager {
private static final String PROPERTY_SRC_ROOT = ".srcRoot"; //$NON-NLS-1$
private static final String PROPERTY_SRC_PATH = ".srcPath"; //$NON-NLS-1$
/**
* Persist the attached sources for given {@link IClasspathEntry} instances.
*/
public static void saveAttachedSources(final IProject project, final IClasspathEntry[] classpathEntries)
throws CoreException {
final Properties props = new Properties();
// Construct the Properties that represent the source attachment(s)
for (final IClasspathEntry entry : classpathEntries) {
if (IClasspathEntry.CPE_LIBRARY != entry.getEntryKind()) {
continue;
}
final String path = entry.getPath()
.toPortableString();
if (entry.getSourceAttachmentPath() != null) {
props.put(path + PROPERTY_SRC_PATH, entry.getSourceAttachmentPath()
.toPortableString());
}
if (entry.getSourceAttachmentRootPath() != null) {
props.put(path + PROPERTY_SRC_ROOT, entry.getSourceAttachmentRootPath()
.toPortableString());
}
}
// Write the properties to a persistent storage area
final File propertiesFile = getSourceAttachmentPropertiesFile(project);
if (props.isEmpty()) {
IO.delete(propertiesFile);
} else {
try (OutputStream out = IO.outputStream(propertiesFile)) {
props.store(out, new Date().toString());
} catch (final IOException e) {
throw new CoreException(new Status(IStatus.ERROR, BndtoolsBuilder.PLUGIN_ID,
"Failure to write container source attachments", e));
}
}
}
/**
* Return (a potentially modified) list of {@link IClasspathEntry} instances
* that will have any previously persisted attached sources added.
*/
public static List loadAttachedSources(final IProject project,
final List classPathEntries) throws CoreException {
if (classPathEntries.isEmpty()) {
return classPathEntries;
}
final List repositories = RepositoryUtils.listRepositories(true);
final Properties props = loadSourceAttachmentProperties(project);
final List configuredClassPathEntries = new ArrayList<>(classPathEntries.size());
for (final IClasspathEntry entry : classPathEntries) {
if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY || entry.getSourceAttachmentPath() != null) {
configuredClassPathEntries.add(entry);
continue;
}
final String key = entry.getPath()
.toPortableString();
IPath srcPath = null;
IPath srcRoot = null;
// Retrieve the saved source attachment information
if (props != null && props.containsKey(key + PROPERTY_SRC_PATH)) {
srcPath = Path.fromPortableString((String) props.get(key + PROPERTY_SRC_PATH));
if (props.containsKey(key + PROPERTY_SRC_ROOT)) {
srcRoot = Path.fromPortableString((String) props.get(key + PROPERTY_SRC_ROOT));
}
} else {
// If there is no saved source attachment, then try and find a
// source bundle
Map extraProps = new HashMap<>();
for (IClasspathAttribute attr : entry.getExtraAttributes()) {
extraProps.put(attr.getName(), attr.getValue());
}
File sourceBundle = getSourceBundle(entry.getPath(), extraProps, repositories);
if (sourceBundle != null) {
srcPath = new Path(sourceBundle.getAbsolutePath());
}
}
if (srcPath != null || srcRoot != null) {
configuredClassPathEntries.add(JavaCore.newLibraryEntry(entry.getPath(), srcPath, srcRoot,
entry.getAccessRules(), entry.getExtraAttributes(), entry.isExported()));
} else {
configuredClassPathEntries.add(entry);
}
}
return configuredClassPathEntries;
}
private static File getSourceBundle(IPath path, Map props, List repositories) {
if (Central.getWorkspaceIfPresent() == null) {
return null;
}
IPath bundlePath = path;
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
IResource resource = root.findMember(path);
if (resource != null) {
bundlePath = resource.getLocation();
}
try (JarInputStream jarStream = new JarInputStream(IO.stream(bundlePath.toFile()), false)) {
Manifest manifest = jarStream.getManifest();
if (manifest == null) {
return null;
}
Domain domain = Domain.domain(manifest);
Entry bsnAttrs = domain.getBundleSymbolicName();
if (bsnAttrs == null) {
return null;
}
String bsn = bsnAttrs.getKey();
String version = domain.getBundleVersion();
if (version == null) {
version = props.get("version");
}
for (RepositoryPlugin repo : repositories) {
if (repo == null) {
continue;
}
if (repo instanceof WorkspaceRepository) {
continue;
}
File sourceBundle = repo.get(bsn + ".source", new Version(version), props);
if (sourceBundle != null) {
return sourceBundle;
}
}
} catch (final Exception e) {
// Ignore, something went wrong, or we could not find the source
// bundle
}
return null;
}
private static Properties loadSourceAttachmentProperties(final IProject project) throws CoreException {
final Properties props = new Properties();
final File propertiesFile = getSourceAttachmentPropertiesFile(project);
if (propertiesFile.exists()) {
try (InputStream in = IO.stream(propertiesFile)) {
props.load(in);
} catch (final IOException e) {
throw new CoreException(new Status(IStatus.ERROR, BndtoolsBuilder.PLUGIN_ID,
"Failure to read container source attachments", e));
}
}
return props;
}
private static File getSourceAttachmentPropertiesFile(final IProject project) {
return new File(BuilderPlugin.getInstance()
.getStateLocation()
.toFile(), project.getName() + ".sources"); //$NON-NLS-1$
}
}