| /******************************************************************************* |
| * Copyright (c) 2004, 2011 Tasktop Technologies 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: |
| * Tasktop Technologies - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.mylyn.internal.context.core; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.mylyn.context.core.IInteractionContext; |
| import org.eclipse.mylyn.context.core.IInteractionContextScaling; |
| import org.eclipse.mylyn.context.core.IInteractionElement; |
| import org.eclipse.mylyn.monitor.core.InteractionEvent; |
| |
| /** |
| * @author Mik Kersten |
| * @author Shawn Minto |
| * @author David Green bug 257977 isInteresting |
| */ |
| public class InteractionContext implements IInteractionContext { |
| |
| private String handleIdentifier; |
| |
| private final List<InteractionEvent> interactionHistory; |
| |
| private final Map<String, InteractionContextElement> elementMap; |
| |
| private final Map<String, IInteractionElement> landmarkMap; |
| |
| /** |
| * The last element that was added to this context. |
| */ |
| private InteractionContextElement activeNode; |
| |
| private InteractionEvent lastEdgeEvent; |
| |
| private InteractionContextElement lastEdgeNode; |
| |
| private String contentLimitedTo; |
| |
| private int numUserEvents; |
| |
| private final IInteractionContextScaling contextScaling; |
| |
| public InteractionContext(String id, IInteractionContextScaling scaling) { |
| this.handleIdentifier = id; |
| this.contextScaling = scaling; |
| this.interactionHistory = new ArrayList<InteractionEvent>(); |
| this.elementMap = new HashMap<String, InteractionContextElement>(); |
| this.landmarkMap = new HashMap<String, IInteractionElement>(); |
| |
| for (InteractionEvent event : interactionHistory) { |
| parseInteractionEvent(event); |
| } |
| |
| for (InteractionContextElement node : elementMap.values()) { |
| if (node.getInterest().isLandmark()) { |
| landmarkMap.put(node.getHandleIdentifier(), node); |
| } |
| } |
| |
| activeNode = lastEdgeNode; |
| } |
| |
| public synchronized void addEvents(IInteractionContext otherContext) { |
| for (InteractionEvent event : otherContext.getInteractionHistory()) { |
| parseEvent(event); |
| } |
| } |
| |
| public synchronized IInteractionElement parseEvent(InteractionEvent event) { |
| interactionHistory.add(event); |
| return parseInteractionEvent(event); |
| } |
| |
| /** |
| * Propagations and predictions are not added as edges |
| */ |
| private IInteractionElement parseInteractionEvent(InteractionEvent event) { |
| if (event.getStructureHandle() == null || event.getKind() == null) { |
| return null; |
| } |
| |
| if (event.getKind().isUserEvent()) { |
| numUserEvents++; |
| } |
| |
| InteractionContextElement node = elementMap.get(event.getStructureHandle()); |
| if (node == null) { |
| if (event instanceof AggregateInteractionEvent) { |
| node = new InteractionContextElement(event.getStructureKind(), event.getStructureHandle(), this, |
| ((AggregateInteractionEvent) event).getEventCountOnCreation()); |
| } else { |
| node = new InteractionContextElement(event.getStructureKind(), event.getStructureHandle(), this); |
| } |
| elementMap.put(event.getStructureHandle(), node); |
| } |
| |
| if (event.getKind().isUserEvent() && event instanceof AggregateInteractionEvent) { |
| // add the rest of the events that this event represented |
| numUserEvents += ((AggregateInteractionEvent) event).getNumCollapsedEvents() - 1; |
| } |
| |
| if (event.getNavigation() != null |
| && !event.getNavigation().equals("null") && lastEdgeEvent != null //$NON-NLS-1$ |
| && lastEdgeNode != null && lastEdgeEvent.getStructureHandle() != null |
| && event.getKind() != InteractionEvent.Kind.PROPAGATION |
| && event.getKind() != InteractionEvent.Kind.PREDICTION) { |
| IInteractionElement navigationSource = elementMap.get(lastEdgeEvent.getStructureHandle()); |
| if (navigationSource != null) { |
| InteractionContextRelation edge = lastEdgeNode.getRelation(event.getStructureHandle()); |
| if (edge == null) { |
| edge = new InteractionContextRelation(event.getStructureKind(), event.getNavigation(), |
| lastEdgeNode, node, this); |
| lastEdgeNode.addEdge(edge); |
| } |
| DegreeOfInterest doi = (DegreeOfInterest) edge.getInterest(); |
| doi.addEvent(event); |
| } |
| } |
| DegreeOfInterest doi = (DegreeOfInterest) node.getInterest(); |
| |
| doi.addEvent(event); |
| if (doi.isLandmark()) { |
| landmarkMap.put(node.getHandleIdentifier(), node); |
| } else { |
| landmarkMap.remove(node.getHandleIdentifier()); // TODO: redundant |
| } |
| if (event.getKind().isUserEvent()) { |
| lastEdgeEvent = event; |
| lastEdgeNode = node; |
| activeNode = node; |
| } |
| return node; |
| } |
| |
| public synchronized IInteractionElement get(String elementHandle) { |
| if (elementHandle == null) { |
| return null; |
| } else { |
| return elementMap.get(elementHandle); |
| } |
| } |
| |
| public synchronized boolean isInteresting(String elementHandle) { |
| InteractionContextElement element = elementMap.get(elementHandle); |
| if (element != null) { |
| return element.getInterest().isInteresting(); |
| } |
| return false; |
| } |
| |
| public synchronized List<IInteractionElement> getInteresting() { |
| List<IInteractionElement> elements = new ArrayList<IInteractionElement>(); |
| for (String key : elementMap.keySet()) { |
| InteractionContextElement info = elementMap.get(key); |
| if (info != null && info.getInterest().isInteresting()) { |
| elements.add(info); |
| } |
| } |
| return elements; |
| } |
| |
| public synchronized List<IInteractionElement> getLandmarks() { |
| return new ArrayList<IInteractionElement>(landmarkMap.values()); |
| } |
| |
| public synchronized void updateElementHandle(IInteractionElement element, String newHandle) { |
| InteractionContextElement currElement = elementMap.remove(element.getHandleIdentifier()); |
| if (currElement != null) { |
| currElement.setHandleIdentifier(newHandle); |
| elementMap.put(newHandle, currElement); |
| } |
| } |
| |
| public synchronized IInteractionElement getActiveNode() { |
| return activeNode; |
| } |
| |
| public synchronized void delete(Collection<IInteractionElement> nodes) { |
| // remove elements |
| Set<String> handlesToRemove = new HashSet<String>(); |
| for (IInteractionElement node : nodes) { |
| handlesToRemove.add(node.getHandleIdentifier()); |
| landmarkMap.remove(node.getHandleIdentifier()); |
| elementMap.remove(node.getHandleIdentifier()); |
| |
| if (activeNode != null && node.getHandleIdentifier().equals(activeNode.getHandleIdentifier())) { |
| activeNode = null; |
| } |
| } |
| |
| // remove events |
| List<InteractionEvent> eventsToRemove = new ArrayList<InteractionEvent>(); |
| for (InteractionEvent event : interactionHistory) { |
| if (handlesToRemove.contains(event.getStructureHandle())) { |
| eventsToRemove.add(event); |
| } |
| } |
| interactionHistory.removeAll(eventsToRemove); |
| } |
| |
| public synchronized void delete(IInteractionElement node) { |
| delete(Collections.singleton(node)); |
| } |
| |
| public synchronized List<IInteractionElement> getAllElements() { |
| return new ArrayList<IInteractionElement>(elementMap.values()); |
| } |
| |
| public String getHandleIdentifier() { |
| return handleIdentifier; |
| } |
| |
| /** |
| * @since 2.1 |
| */ |
| public void setHandleIdentifier(String handle) { |
| this.handleIdentifier = handle; |
| } |
| |
| @Override |
| public String toString() { |
| return handleIdentifier; |
| } |
| |
| public synchronized void reset() { |
| elementMap.clear(); |
| interactionHistory.clear(); |
| landmarkMap.clear(); |
| activeNode = null; |
| numUserEvents = 0; |
| lastEdgeEvent = null; |
| lastEdgeNode = null; |
| } |
| |
| public synchronized int getUserEventCount() { |
| return numUserEvents; |
| } |
| |
| public synchronized List<InteractionEvent> getInteractionHistory() { |
| return new ArrayList<InteractionEvent>(interactionHistory); |
| } |
| |
| public synchronized void collapse() { |
| collapseHistory(interactionHistory); |
| } |
| |
| private synchronized void collapseHistory(List<InteractionEvent> interactionHistoryToCollapseTo) { |
| List<InteractionEvent> collapsedHistory = new ArrayList<InteractionEvent>(); |
| for (InteractionContextElement node : elementMap.values()) { |
| if (!node.equals(activeNode)) { |
| collapseNode(collapsedHistory, node); |
| } |
| } |
| if (activeNode != null) { |
| collapseNode(collapsedHistory, activeNode); |
| } |
| |
| interactionHistoryToCollapseTo.clear(); |
| interactionHistoryToCollapseTo.addAll(collapsedHistory); |
| } |
| |
| /** |
| * This method is only used for asynchronous saving of task contexts |
| * |
| * @return a context with a copy of the collapsed interaction history. All other fields are not set. |
| */ |
| synchronized IInteractionContext createCollapsedWritableCopy() { |
| InteractionContext copiedContext = new InteractionContext(handleIdentifier, contextScaling); |
| copiedContext.contentLimitedTo = contentLimitedTo; |
| |
| collapseHistory(copiedContext.interactionHistory); |
| |
| // none of the following are used for writing contexts so we aren't going to try to copy them |
| //copiedContext.numUserEvents = numUserEvents; |
| // copiedContext.lastEdgeNode = lastEdgeNode; |
| // copiedContext.lastEdgeEvent = lastEdgeEvent; |
| // copiedContext.landmarkMap = landmarkMap; |
| // copiedContext.elementMap = elementMap; |
| // copiedContext.activeNode = activeNode; |
| |
| return copiedContext; |
| } |
| |
| private void collapseNode(List<InteractionEvent> collapsedHistory, InteractionContextElement node) { |
| collapsedHistory.addAll(((DegreeOfInterest) node.getInterest()).getCollapsedEvents()); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| InteractionContext other = (InteractionContext) obj; |
| if (contentLimitedTo == null) { |
| if (other.contentLimitedTo != null) { |
| return false; |
| } |
| } else if (!contentLimitedTo.equals(other.contentLimitedTo)) { |
| return false; |
| } |
| if (handleIdentifier == null) { |
| if (other.handleIdentifier != null) { |
| return false; |
| } |
| } else if (!handleIdentifier.equals(other.handleIdentifier)) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + ((contentLimitedTo == null) ? 0 : contentLimitedTo.hashCode()); |
| result = prime * result + ((handleIdentifier == null) ? 0 : handleIdentifier.hashCode()); |
| return result; |
| } |
| |
| public IInteractionContextScaling getScaling() { |
| return contextScaling; |
| } |
| |
| public String getContentLimitedTo() { |
| return contentLimitedTo; |
| } |
| |
| public void setContentLimitedTo(String contentLimitedTo) { |
| this.contentLimitedTo = contentLimitedTo; |
| } |
| } |