| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.ivyde.internal.eclipse.cpcontainer; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.ivy.Ivy; |
| import org.apache.ivy.core.module.descriptor.Artifact; |
| import org.apache.ivy.core.module.id.ModuleRevisionId; |
| import org.apache.ivy.core.report.ArtifactDownloadReport; |
| import org.apache.ivy.core.resolve.DownloadOptions; |
| import org.apache.ivy.osgi.core.BundleInfo; |
| import org.apache.ivy.osgi.core.ExportPackage; |
| import org.apache.ivy.osgi.core.ManifestParser; |
| import org.apache.ivyde.eclipse.cp.ClasspathSetup; |
| import org.apache.ivyde.eclipse.cp.IvyClasspathContainerConfiguration; |
| import org.apache.ivyde.eclipse.cp.MappingSetup; |
| import org.apache.ivyde.internal.eclipse.IvyDEMessage; |
| import org.apache.ivyde.internal.eclipse.IvyPlugin; |
| import org.apache.ivyde.internal.eclipse.resolve.ResolveResult; |
| import org.apache.ivyde.internal.eclipse.workspaceresolver.WorkspaceResolver; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jdt.core.IAccessRule; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| |
| /** |
| * This class is mapping the resolved artifacts between them. After a resolve process, this class |
| * will build the classpath based on the retrieved artifacts and will search for sources and |
| * javadocs and make them attached. |
| */ |
| public class IvyClasspathContainerMapper { |
| |
| private static final String IVYDE_NS = "http://ant.apache.org/ivy/ivyde/ns/"; |
| |
| private static final String IVYDE_NS_PREFIX = "ivyde:"; |
| |
| private final IProgressMonitor monitor; |
| |
| private final Ivy ivy; |
| |
| private final IJavaProject javaProject; |
| |
| private final Collection<ArtifactDownloadReport> all; |
| |
| private final Map<ModuleRevisionId, Artifact[]> artifactsByDependency; |
| |
| private final Map<ArtifactDownloadReport, Set<String>> retrievedArtifacts; |
| |
| private ClasspathSetup classpathSetup; |
| |
| private MappingSetup mapping; |
| |
| private boolean osgiAvailable; |
| |
| private boolean osgiClasspathAvailable; |
| |
| private IvyAttachementManager attachementManager = IvyPlugin.getDefault() |
| .getIvyAttachementManager(); |
| |
| public IvyClasspathContainerMapper(IProgressMonitor monitor, Ivy ivy, |
| IvyClasspathContainerConfiguration conf, ResolveResult resolveResult) { |
| this.monitor = monitor; |
| this.ivy = ivy; |
| this.javaProject = conf.getJavaProject(); |
| this.classpathSetup = conf.getInheritedClasspathSetup(); |
| this.mapping = conf.getInheritedMappingSetup(); |
| this.all = resolveResult.getArtifactReports(); |
| this.artifactsByDependency = resolveResult.getArtifactsByDependency(); |
| this.retrievedArtifacts = resolveResult.getRetrievedArtifacts(); |
| this.osgiAvailable = IvyPlugin.getDefault().isOsgiAvailable(); |
| this.osgiClasspathAvailable = IvyPlugin.getDefault().isOsgiClasspathAvailable(); |
| } |
| |
| public IClasspathEntry[] map() { |
| IClasspathEntry[] classpathEntries; |
| Collection<IClasspathEntry> paths = new LinkedHashSet<IClasspathEntry>(); |
| |
| IvyDEMessage.verbose("Building classpath from " + all.size() + " resolved artifact(s)"); |
| |
| for (ArtifactDownloadReport artifact : all) { |
| if (artifact.getType().equals(WorkspaceResolver.ECLIPSE_PROJECT_TYPE)) { |
| IvyDEMessage.verbose("Found an workspace dependency on project " |
| + artifact.getName()); |
| // This is a java project in the workspace, add project path |
| // but only add it if it is not a self dependency |
| if (javaProject == null |
| || !artifact.getName().equals(javaProject.getPath().toString())) { |
| IAccessRule[] rules = getAccessRules(javaProject); |
| paths.add(JavaCore.newProjectEntry(new Path(artifact.getName()), rules, true, |
| null, true)); |
| } else { |
| IvyDEMessage.verbose("Skipping self dependency on project " |
| + artifact.getName()); |
| } |
| } else if (artifact.getLocalFile() != null && accept(artifact.getArtifact())) { |
| IvyDEMessage.verbose("Adding " + artifact.getName() + " to the classpath"); |
| |
| // handle unzipped jar with 'Bundle-Classpath' |
| if (osgiClasspathAvailable && artifact.getLocalFile().isDirectory() |
| && classpathSetup.isReadOSGiMetadata()) { |
| File manifestFile = new File(artifact.getLocalFile(), "META-INF/MANIFEST.MF"); |
| if (!manifestFile.exists()) { |
| // no manifest : back to simple classpath |
| paths.add(buildEntry(artifact)); |
| } else { |
| try { |
| BundleInfo bundleInfo = ManifestParser.parseManifest(manifestFile); |
| if (bundleInfo.getClasspath() == null) { |
| // no inner classpath : a simple entry |
| paths.add(buildEntry(artifact)); |
| } else { |
| IAccessRule[] rules = getAccessRules(bundleInfo); |
| for (String innerPath : bundleInfo.getClasspath()) { |
| IClasspathEntry buildEntry = buildEntry(artifact, rules, |
| manifestFile, innerPath); |
| if (buildEntry != null) { |
| paths.add(buildEntry); |
| } |
| } |
| } |
| } catch (IOException e) { |
| IvyDEMessage.error( |
| "Unreadable MANIFEST.MF for artifact " + artifact.getName() + ": " |
| + manifestFile.getAbsolutePath() + " (" + e.getMessage() |
| + ")", e); |
| } catch (ParseException e) { |
| IvyDEMessage.error( |
| "Malformed MANIFEST.MF for artifact " + artifact.getName() + ": " |
| + manifestFile.getAbsolutePath() + " (" + e.getMessage() |
| + ")", e); |
| } |
| } |
| } else { |
| // simple entry |
| paths.add(buildEntry(artifact)); |
| } |
| } |
| |
| } |
| classpathEntries = (IClasspathEntry[]) paths.toArray(new IClasspathEntry[paths.size()]); |
| |
| return classpathEntries; |
| } |
| |
| private IClasspathEntry buildEntry(ArtifactDownloadReport artifact, IAccessRule[] rules, |
| File manifestFile, String innerPath) { |
| IPath classpathArtifact = getArtifactPath(artifact, "/" + innerPath); |
| if (!classpathArtifact.toFile().exists()) { |
| // an non existing inner jar is 'just' a broken MANIFEST.MF, which happens sometimes |
| // with Eclipse bundles |
| IvyDEMessage.warn("The MANIFEST of " + artifact + " (" + manifestFile |
| + ") is referencing a non exitant jar " + classpathArtifact + ". Ignoring it"); |
| return null; |
| } |
| return doBuildEntry(artifact, classpathArtifact, rules); |
| } |
| |
| private IClasspathEntry buildEntry(ArtifactDownloadReport artifact) { |
| IPath classpathArtifact = getArtifactPath(artifact, ""); |
| return doBuildEntry(artifact, classpathArtifact, null); |
| } |
| |
| private IClasspathEntry doBuildEntry(ArtifactDownloadReport artifact, IPath classpathArtifact, |
| IAccessRule[] rules) { |
| IPath sourcesArtifact = getArtifactPath(artifact, sourceArtifactMatcher, |
| mapping.isMapIfOnlyOneSource(), ""); |
| IPath javadocArtifact = getArtifactPath(artifact, javadocArtifactMatcher, |
| mapping.isMapIfOnlyOneJavadoc(), ""); |
| IPath sources = attachementManager.getSourceAttachment(classpathArtifact, sourcesArtifact); |
| IPath sourcesRoot = attachementManager.getSourceAttachmentRoot(classpathArtifact, |
| sourcesArtifact); |
| IClasspathAttribute[] att = getExtraAttribute(classpathArtifact, javadocArtifact); |
| |
| if (sources != null) { |
| IvyDEMessage.debug("Attaching sources " + sources + " to " + classpathArtifact); |
| } |
| if (javadocArtifact != null) { |
| IvyDEMessage.debug("Attaching javadoc " + javadocArtifact + " to " + classpathArtifact); |
| } |
| if (rules != null) { |
| IvyDEMessage.debug("Setting OSGi access rules on " + classpathArtifact); |
| } |
| return JavaCore.newLibraryEntry(classpathArtifact, sources, sourcesRoot, rules, att, false); |
| } |
| |
| private IAccessRule[] getAccessRules(IJavaProject javaProject) { |
| if (!osgiAvailable || !classpathSetup.isReadOSGiMetadata()) { |
| return null; |
| } |
| // TODO |
| // Nicolas: AFAIU, the access rules seems to have to be set on the imported project itself |
| // rather than filtering here, afterwards |
| return null; |
| } |
| |
| private IAccessRule[] getAccessRules(BundleInfo bundleInfo) { |
| if (bundleInfo == null || !classpathSetup.isReadOSGiMetadata()) { |
| return null; |
| } |
| IAccessRule[] rules = new IAccessRule[bundleInfo.getExports().size() + 1]; |
| int i = 0; |
| for (ExportPackage exportPackage : bundleInfo.getExports()) { |
| rules[i++] = JavaCore.newAccessRule( |
| new Path(exportPackage.getName().replace('.', IPath.SEPARATOR) + "/*"), |
| IAccessRule.K_ACCESSIBLE); |
| } |
| rules[i++] = JavaCore.newAccessRule(new Path("**/*"), IAccessRule.K_NON_ACCESSIBLE |
| | IAccessRule.IGNORE_IF_BETTER); |
| return rules; |
| } |
| |
| private Path getArtifactPath(ArtifactDownloadReport artifact, String innerPath) { |
| if (retrievedArtifacts != null) { |
| Set<String> pathSet = retrievedArtifacts.get(artifact); |
| if (pathSet != null && !pathSet.isEmpty()) { |
| return new Path((String) pathSet.iterator().next() + innerPath); |
| } |
| } |
| return new Path(artifact.getLocalFile().getAbsolutePath() + innerPath); |
| } |
| |
| interface ArtifactMatcher { |
| boolean matchName(Artifact artifact, String artifactName); |
| |
| boolean match(Artifact a); |
| |
| String getName(); |
| } |
| |
| private Path getArtifactPath(ArtifactDownloadReport adr, ArtifactMatcher matcher, |
| boolean mapIfOnlyOne, String innerPath) { |
| Artifact artifact = adr.getArtifact(); |
| monitor.subTask("searching " + matcher.getName() + " for " + artifact); |
| for (ArtifactDownloadReport otherAdr : all) { |
| Artifact a = otherAdr.getArtifact(); |
| if (otherAdr.getLocalFile() != null && matcher.matchName(artifact, a.getName()) |
| && a.getModuleRevisionId().equals(artifact.getModuleRevisionId()) |
| && matcher.match(a)) { |
| return getArtifactPath(otherAdr, innerPath); |
| } |
| } |
| // we haven't found source artifact in resolved artifacts, |
| // let's look in the module declaring the artifact |
| ModuleRevisionId mrid = artifact.getId().getModuleRevisionId(); |
| Artifact[] artifacts = (Artifact[]) artifactsByDependency.get(mrid); |
| if (artifacts != null) { |
| Artifact foundArtifact = null; |
| int nbFound = 0; |
| for (int i = 0; i < artifacts.length; i++) { |
| Artifact metaArtifact = artifacts[i]; |
| if (matcher.match(metaArtifact)) { |
| if (matcher.matchName(artifact, metaArtifact.getName())) { |
| // we've found a matching artifact, let's provision it |
| ArtifactDownloadReport metaAdr = ivy.getResolveEngine().download( |
| metaArtifact, new DownloadOptions()); |
| if (metaAdr.getLocalFile() != null && metaAdr.getLocalFile().exists()) { |
| return getArtifactPath(metaAdr, innerPath); |
| } |
| } |
| // keep a reference to the artifact so we could fall back |
| // to map-if-only-one |
| nbFound++; |
| foundArtifact = metaArtifact; |
| } |
| } |
| if (mapIfOnlyOne) { |
| // we haven't found artifact in the module declaring the artifact and having |
| // a matching name. |
| if (nbFound == 1) { |
| // If there is only 1 found artifact, it is the winner ;-) |
| ArtifactDownloadReport metaAdr = ivy.getResolveEngine().download(foundArtifact, |
| new DownloadOptions()); |
| if (metaAdr.getLocalFile() != null && metaAdr.getLocalFile().exists()) { |
| return new Path(metaAdr.getLocalFile().getAbsolutePath()); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private ArtifactMatcher sourceArtifactMatcher = new ArtifactMatcher() { |
| public boolean matchName(Artifact artifact, String source) { |
| return isArtifactName(artifact, source, mapping.getSourceSuffixes(), "source"); |
| } |
| |
| public boolean match(Artifact a) { |
| return mapping.getSourceTypes().contains(a.getType()); |
| } |
| |
| public String getName() { |
| return "sources"; |
| } |
| }; |
| |
| private ArtifactMatcher javadocArtifactMatcher = new ArtifactMatcher() { |
| public boolean matchName(Artifact artifact, String javadoc) { |
| return isArtifactName(artifact, javadoc, mapping.getJavadocSuffixes(), "javadoc"); |
| } |
| |
| public boolean match(Artifact a) { |
| return mapping.getJavadocTypes().contains(a.getType()); |
| } |
| |
| public String getName() { |
| return "javadoc"; |
| } |
| }; |
| |
| private boolean isArtifactName(Artifact artifact, String name, Collection<String> suffixes, |
| String type) { |
| String artifactNameToMatch = (String) artifact.getExtraAttribute(IVYDE_NS_PREFIX + type); |
| if (artifactNameToMatch != null) { |
| // some name is specified, it overrides suffix matching |
| return name.equals(artifactNameToMatch); |
| } |
| String jar = artifact.getName(); |
| if (name.equals(jar)) { |
| return true; |
| } |
| for (String suffix : suffixes) { |
| if (name.equals(jar + suffix)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private IClasspathAttribute[] getExtraAttribute(IPath classpathArtifact, IPath javadocArtifact) { |
| List<IClasspathAttribute> result = new ArrayList<IClasspathAttribute>(); |
| URL url = attachementManager.getDocAttachment(classpathArtifact); |
| |
| if (url == null) { |
| IPath path = javadocArtifact; |
| if (path != null) { |
| String u; |
| try { |
| u = "jar:" + path.toFile().toURI().toURL().toExternalForm() + "!/"; |
| try { |
| url = new URL(u); |
| } catch (MalformedURLException e) { |
| // this should not happen |
| IvyPlugin.logError("The jar URL for the javadoc is not formed correctly " |
| + u, e); |
| } |
| } catch (MalformedURLException e) { |
| // this should not happen |
| IvyPlugin.logError("The path has not a correct URL: " + path, e); |
| } |
| } |
| } |
| |
| if (url != null) { |
| result.add(JavaCore.newClasspathAttribute( |
| IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, url.toExternalForm())); |
| } |
| return result.toArray(new IClasspathAttribute[result.size()]); |
| } |
| |
| /** |
| * Check if the artifact is an artifact which can be added to the classpath container |
| * |
| * @param artifact |
| * the artifact to check |
| * @return <code>true</code> if the artifact can be added |
| */ |
| public boolean accept(Artifact artifact) { |
| boolean accepted = classpathSetup.getAcceptedTypes().contains(artifact.getType()); |
| if (!accepted && classpathSetup.getAcceptedTypes().size() == 1 |
| && classpathSetup.getAcceptedTypes().get(0).equals("*")) { |
| accepted = true; |
| } |
| return accepted && !mapping.getSourceTypes().contains(artifact.getType()) |
| && !mapping.getJavadocTypes().contains(artifact.getType()); |
| } |
| |
| } |