/*******************************************************************************
* Copyright (c) 2009, 2010 SAP AG and others.
* 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:
* SAP AG - initial API and implementation
******************************************************************************/
package de.hpi.sam.bp2009.solution.scopeProvider.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;
import org.eclipse.core.resources.IContainer;
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.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import de.hpi.sam.bp2009.solution.scopeProvider.ProjectBasedScopeProvider;
/**
* <!-- begin-user-doc --> An implementation of the '<em><b>Project Based Scope Provider</b></em>'. <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link ProjectBasedScopeProvider#getInitialProjects <em>Initial Projects</em>}</li>
* <li>{@link ProjectBasedScopeProvider#getInMemoryResources() <em>InMemory Resources</em>}</li>
*
* <li>{@link ProjectBasedScopeProvider#getForwardScopeAsProjects() <em>Forward Scope as Projects</em>}</li>
* <li>{@link ProjectBasedScopeProvider#getForwardScopeAsResources() <em>Forward Scope as Resources</em>}</li>
* <li>{@link ProjectBasedScopeProvider#getForwardScopeAsURIs() <em>Forward Scope as URIs</em>}</li>
*
* <li>{@link ProjectBasedScopeProvider#getBackwardScopeAsProjects() <em>Backward Scope as Projects</em>}</li>
* <li>{@link ProjectBasedScopeProvider#getBackwardScopeAsResources() <em>Backward Scope as Resources</em>}</li>
* <li>{@link ProjectBasedScopeProvider#getBackwardScopeAsURIs() <em>Backward Scope as URIs</em>}</li>
* </ul>
* </p>
*
*/
public class ProjectBasedScopeProviderImpl implements ProjectBasedScopeProvider {
private static final String WARNING_WORKSPACE_IS_CLOSED = "Attention: Workspace is closed. Only objects in the same resourceSet as the initial object(s) are returned as scope.";
protected Collection<IProject> initialProjects = new HashSet<IProject>();
protected WeakHashMap<URI, Object> inMemoryResources = new WeakHashMap<URI, Object>();
protected ResourceSet rs;
protected ProjectBasedScopeProviderImpl() {
super();
}
public ProjectBasedScopeProviderImpl(Notifier... notifiers) {
Collection<Resource> resources = new HashSet<Resource>();
for (Notifier notifier : notifiers) {
if (notifier instanceof EObject) {
EObject eo = (EObject) notifier;
if (eo.eResource() != null) {
resources.add(eo.eResource());
}
} else if (notifier instanceof Resource) {
resources.add((Resource) notifier);
} else if (notifier instanceof ResourceSet) {
resources.addAll(((ResourceSet) notifier).getResources());
rs = (ResourceSet) notifier;
} else {
throw new RuntimeException("Expected Resource, ResourceSet or EObject but got "
+ notifier.getClass().getName());
}
}
setupForResources(resources);
}
@Override
public Collection<IProject> getInitialProjects() {
return initialProjects;
}
@Override
public Collection<Resource> getInMemoryResources() {
return urisAsResources(inMemoryResources.keySet());
}
@Override
public Collection<IProject> getForwardScopeAsProjects() {
return scopeAsProjects(/*forward*/ true);
}
@Override
public Collection<Resource> getForwardScopeAsResources() {
Collection<Resource> result = urisAsResources(scopeAsUris(getForwardScopeAsProjects()));
return result;
}
@Override
public Collection<URI> getForwardScopeAsURIs() {
return scopeAsUris(getForwardScopeAsProjects());
}
public Collection<EObject> getForwardScopeAsEObjects() {
return scopeAsEObjects(getForwardScopeAsResources());
}
@Override
public Collection<IProject> getBackwardScopeAsProjects() {
return scopeAsProjects(/*forward*/ false);
}
@Override
public Collection<Resource> getBackwardScopeAsResources() {
Collection<Resource> result = urisAsResources(scopeAsUris(getBackwardScopeAsProjects()));
return result;
}
@Override
public Collection<URI> getBackwardScopeAsURIs() {
return scopeAsUris(getBackwardScopeAsProjects());
}
public Collection<EObject> getBackwardScopeAsEObjects() {
return scopeAsEObjects(getBackwardScopeAsResources());
}
private void setupForResources(Collection<Resource> list) {
for (Resource res : list) {
ResourceSet set = res.getResourceSet();
if (rs == null && set != null) {
rs = set;
}
URIConverter converter = null;
if (set != null) {
converter = set.getURIConverter();
}
if (converter == null) {
converter = URIConverter.INSTANCE;
}
inMemoryResources.put(res.getURI(), null);
IProject project = getProjectForResource(res, converter);
if (project != null) {
initialProjects.add(project);
}
}
}
private Collection<IFolder> getModelDirectoriesFromProject(IProject project) throws IllegalArgumentException {
Collection<IFolder> folders = new ArrayList<IFolder>();
try {
// refresh: if project is not totally loaded some resources should appear hear
project.refreshLocal(IResource.DEPTH_INFINITE, null);
for (IResource member : project.members(false)) {
if (member instanceof IFolder) {
folders.add((IFolder) member);
}
}
} catch (CoreException e) {
e.printStackTrace();
}
return folders;
}
private Collection<URI> getAllResourcesFromDirectory(IFolder modelDirectory) throws CoreException {
final Set<String> extensions = Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().keySet();
final Collection<URI> resources = new ArrayList<URI>();
for (IResource f : modelDirectory.members()) {
if (f instanceof IContainer) {
continue;
}
// only files with extension 'xmi', 'xml' or one of the registered ones include a resource
if ("xmi".equals(f.getFileExtension()) || "xml".equals(f.getFileExtension())
|| extensions.contains(f.getFileExtension())) {
URI uri = URI.createPlatformResourceURI(f.getProject().getName()+"/"+f.getProjectRelativePath().toString(), /*encoded*/ true);
resources.add(uri);
}
}
return resources;
}
/**
* Project can be null if the scope provider could not resolve project for resource, because resource not in the workspace.
* This commonly happens for metamodels and resources which are in-memory only.
*/
private IProject getProjectForResource(Resource res, URIConverter converter) throws IllegalArgumentException {
if (!Platform.isRunning()) {
workSpaceClosedWarning();
return null;
}
URI uri = converter.normalize(res.getURI());
if (uri.isPlatformResource()) {
String projectName = uri.segment(1);
return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
} else if (uri.isFile()) {
java.net.URI netUri = java.net.URI.create(uri.toString());
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (IFile file : root.findFilesForLocationURI(netUri)) {
if (file.getProject() != null) {
return file.getProject();
}
}
for (IContainer c : root.findContainersForLocationURI(netUri)) {
if (c instanceof IProject) {
return (IProject) c;
}
}
}
return null;
}
private Collection<Resource> urisAsResources(Collection<URI> uris) {
if (rs == null) {
throw new RuntimeException("ScopeProvider not given a ResourceSet. Cannot load resources.");
}
Collection<Resource> result = new ArrayList<Resource>();
for (URI uri : uris) {
result.add(rs.getResource(uri, /*loadOnDemand*/ true));
}
return result;
}
private Collection<IProject> scopeAsProjects(boolean forward) {
Collection<IProject> result = new HashSet<IProject>();
Collection<IProject> pool = new ArrayList<IProject>();
if (Platform.isRunning()) {
pool = Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects());
} else {
workSpaceClosedWarning();
}
for (IProject project : getInitialProjects()) {
try {
result.addAll(recursiveGetReferenceProjectsForProjects(project, result, pool, forward));
} catch (CoreException e) {
e.printStackTrace();
}
}
return result;
}
private void workSpaceClosedWarning() {
// the scope provider was not started as plugin that is why
// only objects in the same resourceSet as the initial object(s) are returned as scope.
System.err.println(WARNING_WORKSPACE_IS_CLOSED);
}
private Collection<URI> scopeAsUris(Collection<IProject> projects) throws IllegalArgumentException {
Collection<URI> result = new HashSet<URI>();
for (IProject project : projects) {
for (IFolder modelDir : getModelDirectoriesFromProject(project)) {
try {
result.addAll(getAllResourcesFromDirectory(modelDir));
} catch (CoreException e) {
e.printStackTrace();
}
}
}
result.addAll(inMemoryResources.keySet());
return result;
}
private Collection<EObject> scopeAsEObjects(Collection<Resource> resources) {
Collection<EObject> result = new HashSet<EObject>();
for (Resource resource : resources) {
Iterator<EObject> iter = resource.getAllContents();
while (iter.hasNext()) {
result.add(iter.next());
}
}
return result;
}
/**
* Computes recursively all referenced or referencing projects from a given pool and initial project
*
* @param project
* project to compute the references for
* @param referencedProjects
* list of all referenced projects, initial empty
* @param pool
* pool of all projects which can be in the references
* @param forward
* switch to decide if one get all reference projects of the given one or all referencing projects
* @return the List of all references
* @throws CoreException
*/
private Collection<IProject> recursiveGetReferenceProjectsForProjects(IProject project,
Collection<IProject> referencedProjects, Collection<IProject> pool, boolean forward) throws CoreException {
/*
* referencing in both directions is reflexive
*/
if (!referencedProjects.contains(project) && pool.contains(project)) {
referencedProjects.add(project);
}
IProject[] refProjects = null;
if (forward) {
refProjects = project.getReferencedProjects();
} else {
refProjects = project.getReferencingProjects();
}
for (IProject referenced : refProjects) {
if (referencedProjects.contains(referenced)) {
continue;
}
if (!pool.contains(referenced)) {
continue;
}
referencedProjects.add(referenced);
recursiveGetReferenceProjectsForProjects(referenced, referencedProjects, pool, forward);
}
return referencedProjects;
}
} // ProjectBasedScopeProviderImpl