/**
* Copyright (c) 2010, 2013 Darmstadt University of Technology.
* 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:
* Olav Lenz - initial API and implementation
*/
package org.eclipse.recommenders.internal.coordinates.rcp;
import static com.google.common.base.Optional.fromNullable;
import static org.eclipse.recommenders.coordinates.rcp.DependencyInfos.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
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.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.recommenders.coordinates.DependencyInfo;
import org.eclipse.recommenders.coordinates.DependencyType;
import org.eclipse.recommenders.coordinates.IDependencyListener;
import org.eclipse.recommenders.coordinates.rcp.DependencyInfos;
import org.eclipse.recommenders.internal.coordinates.rcp.l10n.LogMessages;
import org.eclipse.recommenders.jdt.JavaElementsFinder;
import org.eclipse.recommenders.rcp.JavaModelEvents;
import org.eclipse.recommenders.utils.Logs;
import com.google.common.base.Optional;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
@SuppressWarnings("restriction")
public class EclipseDependencyListener implements IDependencyListener {
private final Multimap<IJavaProject, DependencyInfo> workspaceDependenciesByProject = HashMultimap.create();
private final Multimap<IJavaProject, IPackageFragmentRoot> jrePackageFragmentRoots = HashMultimap.create();
private final BiMap<IJavaProject, DependencyInfo> projectDependencyInfoCache = HashBiMap.create();
public EclipseDependencyListener(final EventBus bus) {
bus.register(this);
parseWorkspaceForDependencies();
}
private void parseWorkspaceForDependencies() {
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (IProject project : projects) {
try {
if (project.isOpen() && project.hasNature(JavaCore.NATURE_ID)) {
IJavaProject javaProject = JavaCore.create(project);
if (javaProject == null) {
continue;
}
registerDependenciesForJavaProject(javaProject);
}
} catch (CoreException e) {
Logs.log(LogMessages.ERROR_FAILED_TO_REGISTER_PROJECT_DEPENDENCIES, e, project);
}
}
}
@Subscribe
public void onEvent(final JavaModelEvents.JavaProjectOpened e) {
registerDependenciesForJavaProject(e.project);
}
@Subscribe
public void onEvent(final JavaModelEvents.JavaProjectClosed e) {
deregisterDependenciesForJavaProject(e.project);
}
@Subscribe
public void onEvent(final JavaModelEvents.JarPackageFragmentRootAdded e) {
registerDependencyForJAR(e.root);
}
@Subscribe
public void onEvent(final JavaModelEvents.JarPackageFragmentRootRemoved e) {
deregisterDependencyForJAR(e.root);
}
private void registerDependenciesForJavaProject(final IJavaProject javaProject) {
DependencyInfo jreDependencyInfo = DependencyInfos.createJreDependencyInfo(javaProject).orNull();
synchronized (this) {
if (jreDependencyInfo != null) {
workspaceDependenciesByProject.put(javaProject, jreDependencyInfo);
jrePackageFragmentRoots.putAll(javaProject, detectJREPackageFragementRoots(javaProject));
}
workspaceDependenciesByProject.putAll(javaProject, searchForAllDependenciesOfProject(javaProject));
cacheProjectDependencyInfo(javaProject);
}
}
private synchronized Set<DependencyInfo> searchForAllDependenciesOfProject(IJavaProject javaProject) {
Set<DependencyInfo> dependencies = new HashSet<>();
Collection<IPackageFragmentRoot> jreRoots = jrePackageFragmentRoots.get(javaProject);
for (IPackageFragmentRoot packageFragmentRoot : JavaElementsFinder.getAllPackageFragmentRoots(javaProject)) {
if (!jreRoots.contains(packageFragmentRoot) && packageFragmentRoot instanceof JarPackageFragmentRoot) {
DependencyInfo dependencyInfo = createJarDependencyInfo(packageFragmentRoot).orNull();
if (dependencyInfo != null) {
dependencies.add(dependencyInfo);
}
} else if (JavaElementsFinder
.getPackageFragmentRootKind(packageFragmentRoot) == IPackageFragmentRoot.K_SOURCE
&& packageFragmentRoot.getJavaProject() != null) {
IJavaProject project = packageFragmentRoot.getJavaProject();
if (project == null) {
continue;
}
DependencyInfo dependencyInfo = DependencyInfos.createProjectDependencyInfo(project).orNull();
if (dependencyInfo != null) {
dependencies.add(dependencyInfo);
}
}
}
return dependencies;
}
public static Set<IPackageFragmentRoot> detectJREPackageFragementRoots(IJavaProject javaProject) {
// Please note that this is merely a heuristic to detect if a Jar is part of the JRE or not:
// All Jars in the JRE_Container which are not located in the ext folder are considered part of the JRE.
Set<IPackageFragmentRoot> jreRoots = new HashSet<IPackageFragmentRoot>();
try {
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
if (entry.getPath().toString().contains("org.eclipse.jdt.launching.JRE_CONTAINER")) { //$NON-NLS-1$
for (IPackageFragmentRoot packageFragmentRoot : javaProject.findPackageFragmentRoots(entry)) {
if (!packageFragmentRoot.getPath().toFile().getParentFile().getName().equals("ext")) { //$NON-NLS-1$
jreRoots.add(packageFragmentRoot);
}
}
}
}
}
} catch (JavaModelException e) {
Logs.log(LogMessages.ERROR_FAILED_TO_DETECT_PROJECT_JRE, e, javaProject);
}
return jreRoots;
}
private synchronized void deregisterDependenciesForJavaProject(IJavaProject javaProject) {
workspaceDependenciesByProject.removeAll(javaProject);
jrePackageFragmentRoots.removeAll(javaProject);
projectDependencyInfoCache.remove(javaProject);
}
private void registerDependencyForJAR(JarPackageFragmentRoot root) {
IJavaProject javaProject = getJavaProjectForPackageFragmentRoot(root).orNull();
if (javaProject == null) {
return;
}
synchronized (this) {
if (!isJREOfProjectKnown(javaProject)) {
workspaceDependenciesByProject.removeAll(javaProject);
projectDependencyInfoCache.remove(javaProject);
registerDependenciesForJavaProject(javaProject);
}
if (!isPartOfTheJRE(root)) {
DependencyInfo dependencyInfo = createJarDependencyInfo(root).orNull();
if (dependencyInfo != null) {
workspaceDependenciesByProject.put(javaProject, dependencyInfo);
cacheProjectDependencyInfo(javaProject);
}
}
}
}
private synchronized boolean isJREOfProjectKnown(IJavaProject javaProject) {
for (DependencyInfo dependencyInfo : workspaceDependenciesByProject.get(javaProject)) {
if (dependencyInfo.getType() == DependencyType.JRE) {
return true;
}
}
return false;
}
private boolean isPartOfTheJRE(IPackageFragmentRoot pfr) {
IJavaProject javaProject = getJavaProjectForPackageFragmentRoot(pfr).orNull();
if (javaProject == null) {
return false;
}
synchronized (this) {
if (!jrePackageFragmentRoots.containsEntry(javaProject, pfr)) {
return false;
}
}
return true;
}
private void deregisterDependencyForJAR(JarPackageFragmentRoot pfr) {
IJavaProject javaProject = getJavaProjectForPackageFragmentRoot(pfr).orNull();
if (javaProject == null) {
return;
}
synchronized (this) {
if (isPartOfTheJRE(pfr)) {
deregisterJREDependenciesForProject(javaProject);
} else {
DependencyInfo jarDependencyInfo = createJarDependencyInfo(pfr).orNull();
if (jarDependencyInfo != null) {
workspaceDependenciesByProject.remove(javaProject, jarDependencyInfo);
}
if (!workspaceDependenciesByProject.containsKey(javaProject)) {
jrePackageFragmentRoots.removeAll(javaProject);
}
}
}
}
private synchronized void deregisterJREDependenciesForProject(IJavaProject javaProject) {
for (DependencyInfo dependencyInfo : workspaceDependenciesByProject.get(javaProject)) {
if (dependencyInfo.getType() == DependencyType.JRE) {
workspaceDependenciesByProject.remove(javaProject, dependencyInfo);
return;
}
}
}
private Optional<IJavaProject> getJavaProjectForPackageFragmentRoot(IPackageFragmentRoot pfr) {
IJavaProject parent = (IJavaProject) pfr.getAncestor(IJavaElement.JAVA_PROJECT);
return fromNullable(parent);
}
private synchronized void cacheProjectDependencyInfo(IJavaProject javaProject) {
DependencyInfo dependencyInfo = projectDependencyInfoCache.get(javaProject);
if (dependencyInfo != null) {
return;
}
dependencyInfo = createProjectDependencyInfo(javaProject).orNull();
if (dependencyInfo == null) {
return;
}
projectDependencyInfoCache.put(javaProject, dependencyInfo);
}
@Override
public synchronized ImmutableSet<DependencyInfo> getDependencies() {
ImmutableSet.Builder<DependencyInfo> res = ImmutableSet.builder();
for (IJavaProject javaProject : workspaceDependenciesByProject.keySet()) {
Collection<DependencyInfo> dependenciesForProject = workspaceDependenciesByProject.get(javaProject);
res.addAll(dependenciesForProject);
}
return res.build();
}
@Override
public synchronized ImmutableSet<DependencyInfo> getDependenciesForProject(DependencyInfo project) {
IJavaProject javaProject = projectDependencyInfoCache.inverse().get(project);
return ImmutableSet.copyOf(workspaceDependenciesByProject.get(javaProject));
}
@Override
public synchronized ImmutableSet<DependencyInfo> getProjects() {
return ImmutableSet.copyOf(projectDependencyInfoCache.inverse().keySet());
}
}