/*******************************************************************************
* Copyright (c) 2012 Wind River Systems 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.debug.test.services;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.eclipse.tcf.protocol.Protocol;
/**
*
*/
public class ResetMap {
public interface IResettable {
public void reset();
}
public static final String ANY_ID = "";
private Map<String, List<IResettable>> fValid = new TreeMap<String, List<IResettable>>();
private Map<String, Set<String>> fChildren = new TreeMap<String, Set<String>>();
private Map<String, String> fParents = new TreeMap<String, String>();
// Mapping of context IDs that were reset while a given cache was pending.
private Map<IResettable, Set<String>> fPending = new LinkedHashMap<IResettable, Set<String>>();
public Set<String> removePending(IResettable cache) {
assert Protocol.isDispatchThread();
Set<String> pendingIds = fPending.remove(cache);
if (pendingIds == null) {
pendingIds = Collections.emptySet();
}
return pendingIds;
}
public boolean clearPending(String id, IResettable cache) {
assert Protocol.isDispatchThread();
Set<String> pendingIds = fPending.remove(cache);
if (pendingIds != null && pendingIds.contains(id)) {
cache.reset();
return true;
}
return false;
}
public void addValid(String id, IResettable cache) {
assert Protocol.isDispatchThread();
if (clearPending(id, cache)) return;
List<IResettable> list = fValid.get(id);
if (list == null) {
list = new LinkedList<IResettable>();
fValid.put(id, list);
}
list.add(cache);
}
public void addValid(String id, String[] childrenIds, IResettable cache) {
assert Protocol.isDispatchThread();
if (clearPending(id, cache)) return;
List<IResettable> list = fValid.get(id);
if (list == null) {
list = new LinkedList<IResettable>();
fValid.put(id, list);
}
list.add(cache);
for (String childId : childrenIds) {
fParents.put(childId, id);
}
}
public void addValid(List<String> ids, IResettable cache) {
assert Protocol.isDispatchThread();
boolean valid = true;
for (int i = 0; i < ids.size() - 1; i++) {
valid = clearPending(ids.get(i), cache) && valid;
}
if (!valid) return;
String id = ids.get(0);
List<IResettable> list = fValid.get(id);
if (list == null) {
list = new ArrayList<IResettable>();
fValid.put(id, list);
}
list.add(cache);
for (int i = 0; i < ids.size() - 1; i++) {
Set<String> children = fChildren.get(ids.get(i + 1));
if (children == null) {
children = new TreeSet<String>();
fChildren.put(ids.get(i + 1), children);
}
children.add(ids.get(i));
}
}
public List<IResettable> getCaches(String id) {
assert Protocol.isDispatchThread();
List<IResettable> list = fValid.get(id);
if (list == null) {
list = Collections.emptyList();
}
return list;
}
public void reset(String id) {
reset(id, true, true);
}
public void reset(String id, boolean resetChildren, boolean resetParents) {
assert Protocol.isDispatchThread();
// Do not call reset while holding lock to reset map. Instead collect
// caches to reset and reset them outside the lock.
List<IResettable> anyList = Collections.emptyList();
List<IResettable> idList = Collections.emptyList();
List<IResettable> parentList = Collections.emptyList();
synchronized (this) {
for (Set<String> pendingIds : fPending.values()) {
pendingIds.add(id);
}
anyList = fValid.remove(ANY_ID);
if (resetChildren && fChildren.containsKey(id)) {
idList = new ArrayList<IResettable>();
collectChildren(id, idList);
} else {
idList = fValid.remove(id);
}
if (resetParents) {
String parentId = fParents.remove(id);
if (parentId != null) {
parentList = fValid.remove(parentId);
}
}
}
resetList(anyList);
resetList(idList);
resetList(parentList);
}
private void collectChildren(String id, List<IResettable> caches) {
caches.addAll( fValid.remove(id) );
Set<String> children = fChildren.remove(id);
if (children != null) {
for (String child : children) {
collectChildren(child, caches);
}
}
}
private void resetList(List<IResettable> list) {
if (list != null) {
for (IResettable cache : list) {
cache.reset();
}
}
}
public void resetAll() {
assert Protocol.isDispatchThread();
Collection<List<IResettable>> valid = null;
synchronized (this) {
valid = fValid.values();
}
for (List<IResettable> validList : valid) {
resetList(validList);
}
}
public void addPending(IResettable cache) {
assert Protocol.isDispatchThread();
if (!fPending.containsKey(cache)) {
fPending.put(cache, new TreeSet<String>());
}
}
}