/********************************************************************** * Copyright (c) 2002, 2015 Geoff Longman 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: * Geoff Longman - Initial API and implementation * IBM - Tightening integration with existing Platform **********************************************************************/ package org.eclipse.core.tools.resources; import org.eclipse.jface.action.Action; import java.util.*; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.tools.SpyView; import org.eclipse.jface.action.*; import org.eclipse.jface.viewers.*; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.*; /** * This spy view displays a tree-based representation of resource deltas from * resource change events. */ public class ResourceChangeView extends SpyView implements IResourceChangeListener { class DeltaNode implements IAdaptable { private ArrayList<DeltaNode> children; private int deltaFlags = -1; private int deltaKind = -1; private DeltaNode parent = null; private IPath path; private IResource resource; public DeltaNode() { children = new ArrayList<DeltaNode>(); } public DeltaNode(IResourceDelta delta) { this(); populateFromDelta(delta); } public void addChild(DeltaNode child) { children.add(child); child.setParent(this); } @Override @SuppressWarnings("unchecked") public <T> T getAdapter(Class<T> key) { if (key != IResource.class) return null; return (T) resource; } public DeltaNode[] getChildren() { return children.toArray(new DeltaNode[children.size()]); } public int getDeltaFlags() { return deltaFlags; } public int getDeltaKind() { return deltaKind; } public DeltaNode getParent() { return parent; } public IPath getPath() { return path; } public IResource getResource() { return resource; } public boolean hasChildren() { return children.size() > 0; } public void populateFromDelta(IResourceDelta delta) { deltaFlags = delta.getFlags(); deltaKind = delta.getKind(); path = delta.getFullPath(); resource = delta.getResource(); IResourceDelta[] deltaChildren = delta.getAffectedChildren(); for (int i = 0; i < deltaChildren.length; i++) addChild(new DeltaNode(deltaChildren[i])); } public void removeChild(DeltaNode child) { children.remove(child); child.setParent(null); } public void setParent(DeltaNode parent) { this.parent = parent; } @Override public String toString() { return resource.getName(); } } class FilterAction extends Action { private int type; public FilterAction(String text, int eventType) { super(text); type = eventType; } /** * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { typeFilter = type; for (Action action : eventActions) { action.setChecked(action.equals(this)); } } } class FilterPhantoms extends Action { public FilterPhantoms() { super("Show Phantoms"); //$NON-NLS-1$ } @Override public void run() { showPhantoms = !showPhantoms; setChecked(showPhantoms); } } class ResourceEventNode extends DeltaNode { private int eventType; public ResourceEventNode(IResourceChangeEvent event) { super(event.getDelta()); eventType = event.getType(); } public int getEventType() { return eventType; } } class ViewContentProvider implements IStructuredContentProvider, ITreeContentProvider { private DeltaNode invisibleRoot; @Override public void dispose() { // do nothing } @Override public Object[] getChildren(Object parent) { return ((DeltaNode) parent).getChildren(); } @Override public Object[] getElements(Object parent) { if (parent.equals(rootObject)) { if (invisibleRoot == null) { if (rootObject instanceof IResourceChangeEvent) { initialize(rootObject); } else { initialize(); } } return getChildren(invisibleRoot); } return getChildren(parent); } @Override public Object getParent(Object child) { return ((DeltaNode) child).getParent(); } @Override public boolean hasChildren(Object parent) { return ((DeltaNode) parent).hasChildren(); } /* * We will set up a dummy model to initialize tree hierarchy. */ private void initialize() { invisibleRoot = new DeltaNode(); } private void initialize(Object input) { if (!(input instanceof IResourceChangeEvent)) return; IResourceChangeEvent evt = (IResourceChangeEvent) input; ResourceEventNode root = new ResourceEventNode(evt); invisibleRoot = new DeltaNode(); invisibleRoot.addChild(root); } @Override public void inputChanged(Viewer v, Object oldInput, Object newInput) { invisibleRoot = null; } } class ViewLabelProvider extends LabelProvider { private String getEventTypeAsString(int eventType) { switch (eventType) { case IResourceChangeEvent.POST_BUILD : return "POST_BUILD"; //$NON-NLS-1$ case IResourceChangeEvent.POST_CHANGE : return "POST_CHANGE"; //$NON-NLS-1$ case IResourceChangeEvent.PRE_BUILD : return "PRE_BUILD"; //$NON-NLS-1$ } return null; } /** * Return a string representation of the change flags found * within a resource change event. */ private String getFlagsAsString(int flags) { StringBuffer buffer = new StringBuffer(); if ((IResourceDelta.ADDED & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("ADDED"); //$NON-NLS-1$ } if ((IResourceDelta.ADDED_PHANTOM & flags) != 0 && showPhantoms) { buffer.append("-"); //$NON-NLS-1$ buffer.append("ADDED_PHANTOM"); //$NON-NLS-1$ } if ((IResourceDelta.ALL_WITH_PHANTOMS & flags) != 0 && showPhantoms) { buffer.append("-"); //$NON-NLS-1$ buffer.append("ALL_WITH_PHANTOMS"); //$NON-NLS-1$ } if ((IResourceDelta.CHANGED & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("CHANGED"); //$NON-NLS-1$ } if ((IResourceDelta.CONTENT & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("CONTENT"); //$NON-NLS-1$ } if ((IResourceDelta.DESCRIPTION & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("DESCRIPTION"); //$NON-NLS-1$ } if ((IResourceDelta.MARKERS & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("MARKERS"); //$NON-NLS-1$ } if ((IResourceDelta.MOVED_FROM & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("MOVED_FROM"); //$NON-NLS-1$ } if ((IResourceDelta.MOVED_TO & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("MOVED_TO"); //$NON-NLS-1$ } if ((IResourceDelta.NO_CHANGE & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("NO_CHANGE"); //$NON-NLS-1$ } if ((IResourceDelta.OPEN & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("OPEN"); //$NON-NLS-1$ } if ((IResourceDelta.REMOVED & flags) != 0) { buffer.append("-"); buffer.append("REMOVED"); } if ((IResourceDelta.REMOVED_PHANTOM & flags) != 0 && showPhantoms) { buffer.append("-"); //$NON-NLS-1$ buffer.append("REMOVED_PHANTOM"); //$NON-NLS-1$ } if ((IResourceDelta.REPLACED & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("REPLACED"); //$NON-NLS-1$ } if ((IResourceDelta.SYNC & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("SYNC"); //$NON-NLS-1$ } if ((IResourceDelta.TYPE & flags) != 0) { buffer.append("-"); //$NON-NLS-1$ buffer.append("TYPE"); //$NON-NLS-1$ } return buffer.toString(); } @Override public Image getImage(Object obj) { DeltaNode node = (DeltaNode) obj; String imageKey; switch (node.getResource().getType()) { case IResource.ROOT : imageKey = ISharedImages.IMG_OBJ_FOLDER; break; case IResource.PROJECT : imageKey = org.eclipse.ui.ide.IDE.SharedImages.IMG_OBJ_PROJECT; break; case IResource.FOLDER : imageKey = ISharedImages.IMG_OBJ_FOLDER; break; case IResource.FILE : default : imageKey = ISharedImages.IMG_OBJ_FILE; break; } return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey); } private String getKindAsString(int kind) { if (kind == -1) return ""; //$NON-NLS-1$ return " kind(" + getFlagsAsString(kind) + ") "; //$NON-NLS-1$ //$NON-NLS-2$ } @Override public String getText(Object obj) { StringBuffer buffer = new StringBuffer(obj.toString()); if (obj instanceof ResourceEventNode) { buffer.append("Workspace Root - "); //$NON-NLS-1$ buffer.append(getEventTypeAsString(((ResourceEventNode) obj).getEventType())); } buffer.append(getKindAsString(((DeltaNode) obj).getDeltaKind())); buffer.append(getFlagsAsString(((DeltaNode) obj).getDeltaFlags())); return buffer.toString(); } } protected Set<Action> eventActions = new HashSet<Action>(); private Action PHANTOMS; private Action POST_BUILD; private Action POST_CHANGE; private Action PRE_BUILD; protected Object rootObject = ResourcesPlugin.getWorkspace(); protected boolean showPhantoms = false; protected int typeFilter = IResourceChangeEvent.POST_CHANGE; protected TreeViewer viewer; /** * The constructor. */ public ResourceChangeView() { super(); } /** * This is a callback that will allow us * to create the viewer and initialize it. */ @Override public void createPartControl(Composite parent) { viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setContentProvider(new ViewContentProvider()); viewer.setLabelProvider(new ViewLabelProvider()); viewer.setInput(rootObject); initializeActions(); IActionBars bars = getViewSite().getActionBars(); IMenuManager menuMgr = bars.getMenuManager(); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager manager) { fillPullDownBar(manager); } }); fillPullDownBar(menuMgr); // register for all types of events and then filter out the one that we // want based on the action settings. ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_BUILD); } /** * @see org.eclipse.ui.IWorkbenchPart#dispose() */ @Override public void dispose() { super.dispose(); ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } /* * The content provider class is responsible for * providing objects to the view. It can wrap * existing objects in adapters or simply return * objects as-is. These objects may be sensitive * to the current input of the view, or ignore * it and always show the same content * (like Task List, for example). */ protected void fillPullDownBar(IMenuManager manager) { manager.removeAll(); for (Action action : eventActions) manager.add(action); manager.add(new Separator("phantoms")); //$NON-NLS-1$ manager.add(PHANTOMS); } private void initializeActions() { // create the actions for the drop-down menu POST_BUILD = new FilterAction("POST_BUILD", IResourceChangeEvent.POST_BUILD); //$NON-NLS-1$ POST_CHANGE = new FilterAction("POST_CHANGE", IResourceChangeEvent.POST_CHANGE); //$NON-NLS-1$ // default event type POST_CHANGE.setChecked(true); PRE_BUILD = new FilterAction("PRE_BUILD", IResourceChangeEvent.PRE_BUILD); //$NON-NLS-1$ eventActions.add(POST_BUILD); eventActions.add(POST_CHANGE); eventActions.add(PRE_BUILD); PHANTOMS = new FilterPhantoms(); } /** * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(IResourceChangeEvent) */ @Override public void resourceChanged(IResourceChangeEvent event) { if (event.getType() != typeFilter) return; setRoot(event); } private void setRoot(IResourceChangeEvent event) { if (viewer == null) return; Display display = viewer.getControl().getDisplay(); if (display == null) return; rootObject = event; display.asyncExec(new Runnable() { @Override public void run() { viewer.getControl().setRedraw(false); viewer.setInput(ResourceChangeView.this.rootObject); viewer.expandAll(); viewer.getControl().setRedraw(true); } }); } }