package com.gratex.perconik.activity.ide.listeners; import java.util.Set; import javax.annotation.concurrent.GuardedBy; import com.google.common.collect.ImmutableSet; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPart; import com.gratex.perconik.activity.uaca.IdeUacaProxy; import com.gratex.perconik.services.uaca.ide.IdeProjectEventRequest; import com.gratex.perconik.services.uaca.ide.IdeProjectEventType; import sk.stuba.fiit.perconik.core.listeners.ResourceListener; import sk.stuba.fiit.perconik.core.listeners.SelectionListener; import sk.stuba.fiit.perconik.eclipse.core.resources.Projects; import sk.stuba.fiit.perconik.eclipse.core.resources.ResourceDeltaFlag; import sk.stuba.fiit.perconik.eclipse.core.resources.ResourceDeltaKind; import sk.stuba.fiit.perconik.eclipse.core.resources.ResourceDeltaResolver; import sk.stuba.fiit.perconik.eclipse.core.resources.ResourceEventType; import sk.stuba.fiit.perconik.eclipse.core.resources.ResourceType; import static com.gratex.perconik.activity.ide.IdeData.setApplicationData; import static com.gratex.perconik.activity.ide.IdeData.setEventData; import static com.gratex.perconik.activity.ide.IdeData.setProjectData; import static com.gratex.perconik.activity.ide.listeners.Utilities.currentTime; import static com.gratex.perconik.activity.ide.listeners.Utilities.isNull; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceDeltaFlag.OPEN; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceDeltaKind.ADDED; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceEventType.POST_CHANGE; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceEventType.PRE_CLOSE; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceEventType.PRE_DELETE; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceEventType.PRE_REFRESH; import static sk.stuba.fiit.perconik.eclipse.core.resources.ResourceType.PROJECT; /** * A listener of IDE project events. This listener handles desired * events and eventually builds corresponding data transfer objects * of type {@link IdeProjectEventRequest} and passes them to the * {@link IdeUacaProxy} to be transferred into the <i>User Activity Central * Application</i> for further processing. * * <p>Project operation types that this listener is interested in are * determined by the {@link IdeProjectEventType} enumeration: * * <ul> * <li>Add - a project is added into the workspace. * <li>Close - an opened project is closed. * <li>Open - a closed project is opened. * <li>Refresh - a project is refreshed. * <li>Remove - a project is removed from the workspace. * <li>Rename - currently not supported. * <li>Switch to - focus is changed from one project to another * and editor selections (tabs and text) are supported. Note that * structured selections in package explorer are not supported. * </ul> * * <p>Data available in an {@code IdeProjectEventRequest}: * * <ul> * <li>See {@link IdeListener} for documentation of inherited data. * </ul> * * @author Pavol Zbell * @since 1.0 */ public final class IdeProjectListener extends IdeListener implements ResourceListener, SelectionListener { // TODO rename not implemented // TODO switch to --> explorer/a editor/b/file explorer/b --> generates switch-to(a,b,a,b) static final boolean processStructuredSelections = false; static final Set<ResourceEventType> resourceEventTypes = ImmutableSet.of(PRE_CLOSE, PRE_DELETE, PRE_REFRESH, POST_CHANGE); private final Object lock = new Object(); @GuardedBy("lock") private IProject project; public IdeProjectListener() {} private boolean updateProject(final IProject project) { if (project != null) { synchronized (this.lock) { if (!project.equals(this.project)) { this.project = project; return true; } } } return false; } static IdeProjectEventRequest build(final long time, final IProject project) { final IdeProjectEventRequest data = new IdeProjectEventRequest(); setProjectData(data, project); setApplicationData(data); setEventData(data, time); return data; } private final class ResourceDeltaVisitor extends ResourceDeltaResolver { private final long time; private final ResourceEventType type; ResourceDeltaVisitor(final long time, final ResourceEventType type) { assert time >= 0 && type != null; this.time = time; this.type = type; } @Override protected boolean resolveDelta(final IResourceDelta delta, final IResource resource) { // // TODO rm // if (IdeApplication.getInstance().isDebug()) { console.put("resource: "+ resource); // console.put(" type: "+ this.type);console.put(" kind: "+ ResourceDeltaKind.valueOf(delta.getKind()).toString()); // console.print(" flags: "+ResourceDeltaFlag.setOf(delta.getFlags()).toString()); } assert delta != null && resource != null; if (ResourceType.valueOf(resource.getType()) != PROJECT) { return true; } if (this.type == POST_CHANGE) { IProject project = (IProject) resource; ResourceDeltaKind kind = ResourceDeltaKind.valueOf(delta.getKind()); Set<ResourceDeltaFlag> flags = ResourceDeltaFlag.setOf(delta.getFlags()); IdeUacaProxy proxy = IdeProjectListener.this.proxy; if (kind == ADDED) { proxy.sendProjectEvent(build(this.time, project), IdeProjectEventType.ADD); } if (flags.contains(OPEN) && project.isOpen()) { proxy.sendProjectEvent(build(this.time, project), IdeProjectEventType.OPEN); } return false; } return this.resolveResource(resource); } @Override protected boolean resolveResource(final IResource resource) { assert ResourceType.valueOf(resource.getType()) == PROJECT; IProject project = (IProject) resource; IdeUacaProxy proxy = IdeProjectListener.this.proxy; switch (this.type) { case PRE_CLOSE: proxy.sendProjectEvent(build(this.time, project), IdeProjectEventType.CLOSE); break; case PRE_DELETE: proxy.sendProjectEvent(build(this.time, project), IdeProjectEventType.REMOVE); break; case PRE_REFRESH: proxy.sendProjectEvent(build(this.time, project), IdeProjectEventType.REFRESH); break; default: break; } return false; } } void processResource(final long time, final IResourceChangeEvent event) { ResourceEventType type = ResourceEventType.valueOf(event.getType()); IResourceDelta delta = event.getDelta(); new ResourceDeltaVisitor(time, type).visitOrProbe(delta, event); } void processSelection(final long time, final IWorkbenchPart part, final ISelection selection) { IProject project = null; if (processStructuredSelections) { if (selection instanceof IStructuredSelection) { project = Projects.fromSelection((IStructuredSelection) selection); } } if (isNull(project) && part instanceof IEditorPart) { project = Projects.fromEditor((IEditorPart) part); } if (isNull(project)) { project = Projects.fromPage(part.getSite().getPage()); } if (this.updateProject(project)) { this.proxy.sendProjectEvent(build(time, project), IdeProjectEventType.SWITCH_TO); } } public void resourceChanged(final IResourceChangeEvent event) { final long time = currentTime(); execute(new Runnable() { public void run() { processResource(time, event); } }); } public void selectionChanged(final IWorkbenchPart part, final ISelection selection) { final long time = currentTime(); execute(new Runnable() { public void run() { processSelection(time, part, selection); } }); } public Set<ResourceEventType> getEventTypes() { return resourceEventTypes; } }