--- /dev/null
+package org.simantics.document.server.client;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+\r
+import org.simantics.db.common.procedure.adapter.ListenerAdapter;\r
+import org.simantics.db.common.procedure.adapter.ListenerSupport;\r
+import org.simantics.db.layer0.variable.ProxyChildVariable;\r
+import org.simantics.document.server.DocumentHistoryCollector;\r
+import org.simantics.document.server.JSONObject;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+\r
+abstract public class DocumentClient implements Document {\r
+\r
+ protected HashMap<String, WidgetData> widgetData = new HashMap<String, WidgetData>();\r
+ private WidgetMapping mapping;\r
+ private CommandMapping commandMapping;\r
+ \r
+ public DocumentClient(WidgetMapping mapping, CommandMapping commandMapping) {\r
+ this.mapping = mapping;\r
+ this.commandMapping = commandMapping;\r
+ }\r
+\r
+ @Override\r
+ public WidgetManager<?, ?> getManager(JSONObject object) {\r
+ return mapping.getWidget(object.getType());\r
+ }\r
+ \r
+ @Override\r
+ public WidgetData getWidget(String id) {\r
+ return widgetData.get(id);\r
+ }\r
+ \r
+ abstract public IThreadWorkQueue thread();\r
+ \r
+ protected void updateDocument(Collection<JSONObject> objects) {\r
+\r
+ ArrayList<WidgetData> updatedData = new ArrayList<WidgetData>();\r
+ // Cache data\r
+ for (JSONObject object : objects) {\r
+ WidgetData datum = widgetData.get(object.getId());\r
+ if(datum == null) {\r
+ datum = new WidgetData(this, null, object);\r
+ widgetData.put(object.getId(), datum);\r
+ } else {\r
+ // This could replace changed information instead of replacing the whole object\r
+ datum.object = object;\r
+ }\r
+ updatedData.add(datum);\r
+ }\r
+\r
+ // Create widgets\r
+ for(WidgetData datum : updatedData) {\r
+ createElementIfNecessary(datum);\r
+ }\r
+ \r
+ HashSet<WidgetData> updatedParents = new HashSet<WidgetData>(); \r
+ \r
+ // Update parent hierarchy\r
+ for(WidgetData datum : updatedData) {\r
+ updateChildmaps(datum, updatedParents);\r
+ }\r
+ \r
+ updateTree(updatedParents);\r
+ \r
+ // Set values\r
+ for(WidgetData datum : updatedData) {\r
+ datum.updateProperties();\r
+ datum.updateCommands();\r
+ }\r
+\r
+ }\r
+ \r
+ private void createElementIfNecessary(WidgetData data) {\r
+ if(data.widget == null) { \r
+ data.widget = data.createElement();\r
+ }\r
+ }\r
+\r
+ private void updateChildmaps(WidgetData data, HashSet<WidgetData> updates) {\r
+\r
+ // Root\r
+ if(data.object == null) return;\r
+ \r
+// System.err.println("p:" + data.object);\r
+// Object o = data.object.getParent();\r
+ \r
+ WidgetData parent = widgetData.get(data.object.getParent()); \r
+\r
+// System.out.println("parent:" + parent);\r
+\r
+ if(parent == null) return;\r
+ \r
+ String parentOrd = data.object.getParentOrd();\r
+ \r
+ WidgetData existing = parent.childmap.get(parentOrd);\r
+ if(!data.equals(existing)) {\r
+ updates.add(parent);\r
+ parent.childmap.put(parentOrd, data);\r
+ }\r
+ \r
+ }\r
+\r
+ protected void updateTree(HashSet<WidgetData> updates) {\r
+ \r
+ if(updates.isEmpty()) return;\r
+ \r
+ ArrayList<WidgetData> updateList = new ArrayList<WidgetData>(updates);\r
+ Collections.sort(updateList, new Comparator<WidgetData>() {\r
+\r
+ private boolean isParent(WidgetData data, WidgetData possibleParent) {\r
+ WidgetData parent = widgetData.get(data.object.getParent());\r
+ if(parent == null) return false;\r
+ else if(parent == possibleParent) return true;\r
+ else return isParent(parent, possibleParent);\r
+ }\r
+ \r
+ @Override\r
+ public int compare(WidgetData arg0, WidgetData arg1) {\r
+ if(arg0 == arg1) return 0;\r
+ else if( isParent(arg0, arg1) ) return 1;\r
+ else if( isParent(arg1, arg0) ) return -1;\r
+ else return 0;\r
+ }\r
+ \r
+ });\r
+ \r
+ for(WidgetData d : updateList) d.updateChildren();\r
+ \r
+ }\r
+ \r
+ public class DocumentListener extends ListenerAdapter<Integer> {\r
+\r
+ int revision = -1;\r
+ \r
+ final private ListenerSupport support;\r
+ final private String document;\r
+ final private String input;\r
+ private boolean performing = false;\r
+ \r
+ public DocumentListener(ListenerSupport support, String document, String input) {\r
+ this.support = support;\r
+ this.document = document;\r
+ this.input = input;\r
+ }\r
+ \r
+ public synchronized void perform() {\r
+ if(performing) return;\r
+ performing = true;\r
+ try {\r
+ Pair<Integer, Collection<JSONObject>> content = DocumentHistoryCollector.getContent(DocumentListener.this, document + "/" + ProxyChildVariable.CONTEXT_BEGIN + input + "/" + ProxyChildVariable.CONTEXT_END, revision);\r
+ revision = content.first;\r
+ updateDocument(content.second);\r
+ } finally {\r
+ performing = false;\r
+ }\r
+ }\r
+ \r
+ Runnable updater = new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+ perform();\r
+// Pair<Integer, Collection<JSONObject>> content = DocumentHistoryCollector.getContent(DocumentListener.this, document + "/???" + input + "/???", revision);\r
+// revision = content.first;\r
+// updateDocument(content.second);\r
+ }\r
+ \r
+ };\r
+ \r
+ @Override\r
+ public void execute(Integer result) {\r
+ IThreadWorkQueue thread = thread();\r
+ if(thread.currentThreadAccess()) thread.syncExec(updater);\r
+ else thread.asyncExec(updater);\r
+ }\r
+ \r
+ @Override\r
+ public boolean isDisposed() {\r
+ return support.isDisposed();\r
+ }\r
+\r
+ };\r
+ \r
+ public void track(ListenerSupport support, String document, String input) {\r
+ \r
+ new DocumentListener(support, document, input).perform();\r
+ \r
+// DocumentHistoryCollector.register(location, listener);\r
+// listener.execute(-1);\r
+ \r
+ }\r
+ \r
+ WidgetData getRoot() {\r
+ return widgetData.get("root");\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ <T> T getRootWidget() {\r
+ return (T)getRoot().widget;\r
+ }\r
+ \r
+ @Override\r
+ public CommandManager<?, ?> getCommandManager(JSONObject object) {\r
+ return commandMapping.getCommandManager(object.getType());\r
+ }\r
+ \r
+}\r