package org.activiti.designer.util.workspace;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.activiti.bpmn.model.Process;
import org.activiti.designer.util.ActivitiConstants;
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.IResourceVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
public class ActivitiWorkspaceUtil {
/**
* A cache of all project data files and some useful cache data like process IDs within this
* project
*/
private static Map<IResource, CacheData> cache = new HashMap<IResource, CacheData>();
/**
* Returns a set of all open activiti projects found in the workspace.
*
* @return a set of projects with the nature, or an empty set if none are found (which would be
* weird as at least one should be open to call this method ;-)
*/
public static final Set<IProject> getOpenProjects() {
final Set<IProject> result = new HashSet<IProject>();
final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (final IProject project : projects) {
try {
if (project.isOpen() && project.hasNature(ActivitiConstants.NATURE_ID)) {
result.add(project);
}
} catch (CoreException exception) {
// intentionally left blank
}
}
return result;
}
/**
* Retrieves all process IDs in the given diagram data file resource. In case the resource does
* not reflect a data file for a BPMN process, the result will be empty.
*
* @param dataFile the data file of the diagram to use
* @return all process IDs the data file contains
*/
private static Set<String> getProcessIds(final IFile dataFile) {
final Set<String> result = new HashSet<String>();
final BpmnProcessParser parser = new BpmnProcessParser(dataFile);
for (Process process : parser.getProcesses()) {
result.add(process.getId());
}
return result;
}
/**
* Returns all found diagram data files over all open activiti projects. Additionally this method
* will build a cache for these data files, where for each entry all process IDs existing in the
* cache are saved for faster retrieval, as retrieving process IDs involves calling the
* {@link BpmnProcessParser}.
*
* @return a set of all diagram data files in all open projects
*/
public static final Set<IFile> getAllDiagramDataFiles() {
final Set<IFile> result = new HashSet<IFile>();
final Set<IProject> projects = ActivitiWorkspaceUtil.getOpenProjects();
for (final IProject project : projects) {
final DiagramDataFileFinder visitor = new DiagramDataFileFinder();
try {
project.accept(visitor);
} catch (CoreException exception) {
// intentionally ignored
}
for (final IFile resource : visitor.getResources()) {
result.add(resource);
CacheData cachedResourceData = cache.get(resource);
final long lastModified = resource.getModificationStamp();
if (cachedResourceData == null || cachedResourceData.cacheIsExpired(lastModified)) {
final Set<String> processIds = getProcessIds(resource);
if (cachedResourceData == null) {
cachedResourceData = new CacheData(processIds, lastModified);
cache.put(resource, cachedResourceData);
} else {
cachedResourceData.setProcessIds(processIds);
cachedResourceData.setLastModified(lastModified);
}
}
}
}
return result;
}
/**
* Maps all currently found diagrams in all open activiti projects to their included process IDs.
*
* @return a map where the key is the data file resource of a diagram and the value is a set of
* all processes defined in this diagram.
*/
public static final Map<IFile, Set<String>> getAllProcessIdsByDiagramDataFile() {
final Map<IFile, Set<String>> result = new HashMap<IFile, Set<String>>();
final Set<IFile> projectResources = getAllDiagramDataFiles();
for (final IFile projectResource : projectResources) {
result.put(projectResource, cache.get(projectResource).getProcessIds());
}
return result;
}
/**
* Returns the diagram data files that match the given process ID.
*
* @param processId the process ID to look for
* @return a set of diagram data files or <code>null</code> in case no such process ID exists in
* any diagram.
*/
public static final Set<IFile> getDiagramDataFilesByProcessId(final String processId) {
final Set<IFile> result = new HashSet<IFile>();
final Set<IFile> projectResources = getAllDiagramDataFiles();
for (final IFile resource : projectResources) {
final CacheData data = cache.get(resource);
if (data.hasProcessId(processId)) {
result.add(resource);
}
}
return result;
}
/**
* A resource visitor to find all activiti diagram files within a project. This visitor is
* applied to each open Activiti project.
*/
private static class DiagramDataFileFinder implements IResourceVisitor {
private static final Set<String> IGNORED_ROOT_SEGMENTS = new HashSet<String>();
private Set<IFile> visitResults = new HashSet<IFile>();
static {
IGNORED_ROOT_SEGMENTS.add("target");
IGNORED_ROOT_SEGMENTS.add("tempbar");
}
@Override
public boolean visit(IResource resource) throws CoreException {
if (isIgnoredResource(resource)) {
return false;
}
if (resource instanceof IFile
&& resource.getName().endsWith(ActivitiConstants.DATA_FILE_EXTENSION)) {
visitResults.add((IFile) resource);
}
return true;
}
private boolean isIgnoredResource(IResource resource) {
boolean result = false;
if (resource instanceof IFolder) {
final String rootSegment = ((IFolder) resource).getFullPath().segment(1);
if (IGNORED_ROOT_SEGMENTS.contains(rootSegment)) {
result = true;
}
}
return result;
}
public Set<IFile> getResources() {
return visitResults;
}
}
/**
* A data cache to cache process IDs for currently open diagrams as this involves calling the
* {@link BpmnProcessParser}, which is rather time consuming.
*/
private static class CacheData {
private Set<String> processIds;
private Long lastModified;
public CacheData(final Set<String> bpmnIds, final Long lastModified) {
this.processIds = bpmnIds;
this.lastModified = lastModified;
}
public void setProcessIds(Set<String> processIds) {
this.processIds = processIds;
}
public Set<String> getProcessIds() {
return processIds;
}
public void setLastModified(Long lastModified) {
this.lastModified = lastModified;
}
public boolean cacheIsExpired(final Long lastModified) {
return this.lastModified.compareTo(lastModified) < 0;
}
public boolean hasProcessId(final String processId) {
return processIds.contains(processId);
}
}
}