/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.browsing.ui.common.internal; import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.Set; import org.simantics.browsing.ui.DataSource; import org.simantics.browsing.ui.NodeContext; import org.simantics.browsing.ui.NodeContext.CacheKey; import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey; import org.simantics.browsing.ui.NodeContext.QueryKey; import org.simantics.browsing.ui.NodeQueryManager; import org.simantics.browsing.ui.NodeQueryProcessor; import org.simantics.browsing.ui.PrimitiveQueryProcessor; import org.simantics.browsing.ui.PrimitiveQueryUpdater; import org.simantics.browsing.ui.common.internal.IGECache.IGECacheEntry; import org.simantics.browsing.ui.exception.NoDataSourceException; import org.simantics.browsing.ui.exception.NoQueryProcessorException; public class GENodeQueryManager implements NodeQueryManager, PrimitiveQueryUpdater { private static final boolean DEBUG = false; protected IGraphExplorerContext ge; protected NodeContext parentContext; protected CacheKey parentKey; protected UIElementReference treeReference; protected boolean disposed = false; List children = new ArrayList(); void indent() { ge.queryIndent(1); } void unindent() { ge.queryIndent(-1); } void queryDebug(String s) { for (int i = 0; i < ge.queryIndent(); ++i) System.err.append(" "); System.err.println(s); } String toString(UIElementReference ref) { return ref == null ? "" : ref.toString(); } String toString(Set refs) { if (refs == null || refs.isEmpty()) return "{}"; StringBuilder b = new StringBuilder(); for (UIElementReference ref : refs) { b.append(toString(ref)); } return b.toString(); } String toString(NodeContext ctx, CacheKey key) { if (ctx == null) return "null"; Set refs = ge.getCache().getTreeReference(ctx, key); //return String.valueOf(System.identityHashCode(ctx)) + toString(ref); //return String.valueOf(ctx.hashCode()) + ":" + String.valueOf(System.identityHashCode(ctx)) + toString(ref); return ctx + toString(refs); } public GENodeQueryManager(GENodeQueryManager parent, NodeContext parentContext, CacheKey parentKey, UIElementReference treeReference) { this.ge = parent.ge; this.parentContext = parentContext; this.parentKey = parentKey; this.treeReference = treeReference; parent.children.add(this); } public GENodeQueryManager(IGraphExplorerContext ge, NodeContext parentContext, CacheKey parentKey, UIElementReference treeReference) { this.ge = ge; this.parentContext = parentContext; this.parentKey = parentKey; this.treeReference = treeReference; } @Override public Object getExplorerContext() { return ge; } public void dispose() { if (disposed) return; disposed = true; if (ge != null && parentContext != null && parentKey != null) { ge.getCache().remove(parentContext, parentKey); } ge = null; parentContext = null; parentKey = null; treeReference = null; for (GENodeQueryManager m : children) m.dispose(); children.clear(); children = null; } // @Override // public String toString() { // return "GENodeQueryManager[parentKey=" + parentKey + ", parentContext=" + "]"; // } // // @Override // public CacheKey getParentKey() { // return parentKey; // } // // @Override // public INodeContext getParentContext() { // return parentContext; // } // static int koss = 0; // @Override public void replaceResult(NodeContext context, PrimitiveQueryKey key, T newResult, int indent) { IGraphExplorerContext ge = this.ge; if (isDisposed()) return; if(DEBUG) { queryDebug("replaceResult[" + ge.getCache().hashCode() + "] " + key + " -> " + newResult); indent(); } // if((koss++ % 5000) == 0) { // System.out.println("R" + koss); // } IGECache cache = ge.getCache(); IGECacheEntry oldEntry = cache.getEntry(context, key); if (oldEntry != null) { cache.put(context, key, newResult); propagate(context, key, oldEntry, indent); Set refs = cache.removeTreeReference(context, key); if (refs != null) { //queryDebug("(replaceResult) found tree references " + toString(refs)); for (UIElementReference ref : refs) ge.update(ref); } } else { // TODO: explain why this check is here or remove it! // Consistency checking, no TreeReference should ever exist in this case! Set ref = cache.getTreeReference(context, key); assert ref == null; } } // @Override public void clearResult(NodeContext context, CacheKey key, int indent) { // if (key == BuiltinKeys.FINAL_CHILDREN) { // queryDebug("Clear final children for " + context); // } if(DEBUG) queryDebug("clearResult[" + ge.getCache().hashCode() + "] " + key + " " + context); IGraphExplorerContext ge = this.ge; if (isDisposed()) return; IGECache cache = ge.getCache(); IGECacheEntry entry = cache.getEntry(context, key); if (entry != null) { cache.remove(context, key); propagate(context, key, entry, indent); } Set refs = cache.removeTreeReference(context, key); if (refs != null) { //queryDebug("(clearResult) found tree reference " + toString(refs)); for (UIElementReference ref : refs) ge.update(ref); } } public void propagate(NodeContext context, CacheKey key, IGECacheEntry entry, int indent) { if (isDisposed()) return; if(DEBUG) queryDebug("propagate[" + ge.getCache().hashCode() + "] " + key + " - " + context); assert entry != null; for(IGECacheEntry dependency : entry.getDependencies()) { clearResult(dependency.getContext(), dependency.getKey(), indent + 3); } entry.reset(); } @SuppressWarnings("unchecked") @Override public T query(NodeContext context, QueryKey key) throws NoQueryProcessorException { assert(!ge.isDisposed()); if(DEBUG) { queryDebug("Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, parentKey)); indent(); } assert(!(context == parentContext && key == parentKey)); assert(context != null); assert(key != null); T result = null; IGECache cache = ge.getCache(); synchronized(ge.getPropagateLock()) { IGECacheEntry entry = cache.getEntry(context, key); //queryDebug(" CACHED RESULT: " + entry); if(entry == null) { entry = cache.put(context, key, null); NodeQueryProcessor processor = ge.getProcessor(key); if(processor == null) { throw new NoQueryProcessorException(key); } // queryDebug("PERFORMING QUERY..."); T value = processor.query(new GENodeQueryManager(this, context, key, null), context); // queryDebug("RESULT: " + value); entry.setValue(value); } if(treeReference != null) { UIElementReference cachedTreeReference = treeReference; Set oldRefs = cache.getTreeReference(context, key); if (oldRefs != null) { if (cachedTreeReference.isDisposed()) { oldRefs.remove(cachedTreeReference); } else { cache.putTreeReference(context, key, cachedTreeReference); } } else { cache.putTreeReference(context, key, cachedTreeReference); } } if(parentContext != null) { assert(parentKey != null); IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey); if(parentEntry != null) entry.addDependency(parentEntry); } result = (T) entry.getValue(); } unindent(); return result; } @SuppressWarnings("unchecked") @Override public T query(NodeContext context, PrimitiveQueryKey key) throws NoQueryProcessorException { assert(!ge.isDisposed()); if(DEBUG) { queryDebug("Primitive Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, key) + " " + Thread.currentThread().getName()); indent(); } assert(!(context == parentContext && key == parentKey)); // Primitive queries must be leaf queries! assert(!(parentKey instanceof PrimitiveQueryKey)); assert(context != null); assert(key != null); T result = null; IGECache cache = ge.getCache(); synchronized(ge.getPropagateLock()) { IGECacheEntry entry = cache.getEntry(context, key); if(DEBUG) queryDebug(" CACHED PRIMITIVE RESULT[" + cache.hashCode() + "]: " + ((entry != null) ? (entry.hashCode() + "|" + System.identityHashCode(entry)) : 0)); if(entry == null) { entry = cache.put(context, key, null); PrimitiveQueryProcessor processor = ge.getPrimitiveProcessor(key.processorIdenfitier()); if(processor == null) { throw new NoQueryProcessorException(key); } // queryDebug("PERFORMING PRIMITIVE QUERY..."); T value = processor.query(new GENodeQueryManager(this, context, key, null), context, key); // queryDebug("PRIMITIVE RESULT: " + value); entry.setValue(value); } if(treeReference != null) { UIElementReference cachedTreeReference = treeReference; Set oldRefs = cache.getTreeReference(context, key); if (oldRefs != null) { if (cachedTreeReference.isDisposed()) { oldRefs.remove(cachedTreeReference); } else { cache.putTreeReference(context, key, cachedTreeReference); } } else { cache.putTreeReference(context, key, cachedTreeReference); } } if(parentContext != null) { assert(parentKey != null); IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey); if(parentEntry != null) { entry.addDependency(parentEntry); } } result = (T) entry.getValue(); } unindent(); return result; } @Override public DataSource tryGetDataSource(Class clazz) { return ge.getDataSource(clazz); } @Override public DataSource getDataSource(Class clazz) { DataSource dsp = ge.getDataSource(clazz); if (dsp == null) throw new NoDataSourceException(clazz); return dsp; } // @Override // public void scheduleClear(final INodeContext context, final PrimitiveQueryKey key) { // ge.scheduler.execute(new Runnable() { // @Override // public void run() { // synchronized(ge.propagate) { // clearResult(context, key, 0); // } // } // }); // } // @Override // public void create(final INodeContext context, final PrimitiveQueryKey key, final T newResult) { // ge.cache.put(context, key, newResult); // } @Override public void scheduleReplace(final NodeContext context, final PrimitiveQueryKey key, final T newResult) { if(DEBUG) queryDebug("scheduleReplace[" + ge.getCache().hashCode() + "] context=" + context + " key=" + key); IGraphExplorerContext ge = this.ge; if (isDisposed()) return; class PropagateRunner implements Runnable { @Override public void run() { IGraphExplorerContext ge = GENodeQueryManager.this.ge; if (isDisposed()) return; int delay = 0; List todo = null; synchronized(ge.getPropagateListLock()) { ge.setPropagating(true); List scheduleList = ge.getScheduleList(); Deque activity = ge.getActivity(); activity.addFirst(scheduleList.size()); activity.pollLast(); int activityInt = 0; for(int i : activity) { activityInt += i; } ge.setActivityInt(activityInt); if(activityInt < 100) { delay = 10; //System.out.println("Scheduling propagate after 10ms."); } else if (activityInt < 1000) { delay = 500; //System.out.println("Scheduling propagate after 500ms."); } else { delay = 3000; //System.out.println("Scheduling propagate after 3000ms."); } todo = ge.getScheduleList(); ge.setScheduleList(new ArrayList()); } try { if(delay > 0) Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } if (isDisposed()) return; synchronized(ge.getPropagateLock()) { for(Runnable r : todo) r.run(); } if (isDisposed()) return; synchronized(ge.getPropagateListLock()) { ge.setPropagating(false); if(!ge.getScheduleList().isEmpty()) ge.scheduleQueryUpdate(new PropagateRunner()); } } } synchronized(ge.getPropagateListLock()) { // System.out.println("Schedule Replace: " + key + " - " + context); // new Exception().printStackTrace(); List scheduleList = ge.getScheduleList(); scheduleList.add(new Runnable() { @Override public void run() { replaceResult(context, key, newResult, 0); } }); if(ge.isPropagating()) return; ge.scheduleQueryUpdate(new PropagateRunner()); } } @Override public boolean isDisposed() { if (disposed) return true; if (ge.isDisposed()) { dispose(); return true; } return false; } @Override public boolean isShown(NodeContext context) { IGraphExplorerContext ge = this.ge; if (isDisposed()) return false; return ge.getCache().isShown(context); } @Override public void incRef(NodeContext context) { IGraphExplorerContext ge = this.ge; if (isDisposed()) return; ge.getCache().incRef(context); } @Override public void decRef(NodeContext context) { IGraphExplorerContext ge = this.ge; if (isDisposed()) return; ge.getCache().decRef(context); } }