1 package org.simantics.document.server.client;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.HashMap;
8 import java.util.HashSet;
10 import org.simantics.db.common.procedure.adapter.ListenerAdapter;
11 import org.simantics.db.common.procedure.adapter.ListenerSupport;
12 import org.simantics.db.layer0.variable.ProxyChildVariable;
13 import org.simantics.document.server.DocumentHistoryCollector;
14 import org.simantics.document.server.JSONObject;
15 import org.simantics.utils.datastructures.Pair;
16 import org.simantics.utils.threads.IThreadWorkQueue;
18 abstract public class DocumentClient implements Document {
20 protected HashMap<String, WidgetData> widgetData = new HashMap<String, WidgetData>();
21 private WidgetMapping mapping;
22 private CommandMapping commandMapping;
24 public DocumentClient(WidgetMapping mapping, CommandMapping commandMapping) {
25 this.mapping = mapping;
26 this.commandMapping = commandMapping;
30 public WidgetManager<?, ?> getManager(JSONObject object) {
31 WidgetManager<?, ?> result = mapping.getWidget(object.getType());
33 throw new RuntimeException("No manager for type " + object.getType());
38 public WidgetData getWidget(String id) {
39 return widgetData.get(id);
42 abstract public IThreadWorkQueue thread();
44 protected void updateDocument(Collection<JSONObject> objects) {
46 ArrayList<WidgetData> updatedData = new ArrayList<WidgetData>();
48 for (JSONObject object : objects) {
49 WidgetData datum = widgetData.get(object.getId());
51 datum = new WidgetData(this, null, object);
52 widgetData.put(object.getId(), datum);
54 // This could replace changed information instead of replacing the whole object
55 datum.object = object;
57 updatedData.add(datum);
61 for(WidgetData datum : updatedData) {
62 createElementIfNecessary(datum);
65 HashSet<WidgetData> updatedParents = new HashSet<WidgetData>();
67 // Update parent hierarchy
68 for(WidgetData datum : updatedData) {
69 updateChildmaps(datum, updatedParents);
72 updateTree(updatedParents);
75 for(WidgetData datum : updatedData) {
76 datum.updateProperties();
77 datum.updateCommands();
82 private void createElementIfNecessary(WidgetData data) {
83 if(data.widget == null) {
84 data.widget = data.createElement();
88 private void updateChildmaps(WidgetData data, HashSet<WidgetData> updates) {
91 if(data.object == null) return;
93 // System.err.println("p:" + data.object);
94 // Object o = data.object.getParent();
96 WidgetData parent = widgetData.get(data.object.getParent());
98 // System.out.println("parent:" + parent);
100 if(parent == null) return;
102 String parentOrd = data.object.getParentOrd();
104 WidgetData existing = parent.childmap.get(parentOrd);
105 if(!data.equals(existing)) {
107 parent.childmap.put(parentOrd, data);
112 protected void updateTree(HashSet<WidgetData> updates) {
114 if(updates.isEmpty()) return;
116 ArrayList<WidgetData> updateList = new ArrayList<WidgetData>(updates);
117 Collections.sort(updateList, new Comparator<WidgetData>() {
119 private boolean isParent(WidgetData data, WidgetData possibleParent) {
120 WidgetData parent = widgetData.get(data.object.getParent());
121 if(parent == null) return false;
122 else if(parent == possibleParent) return true;
123 else return isParent(parent, possibleParent);
127 public int compare(WidgetData arg0, WidgetData arg1) {
128 if(arg0 == arg1) return 0;
129 else if( isParent(arg0, arg1) ) return 1;
130 else if( isParent(arg1, arg0) ) return -1;
136 for(WidgetData d : updateList) d.updateChildren();
140 public class DocumentListener extends ListenerAdapter<Integer> {
144 final private ListenerSupport support;
145 final private String document;
146 final private String input;
147 private boolean performing = false;
149 public DocumentListener(ListenerSupport support, String document, String input) {
150 this.support = support;
151 this.document = document;
155 public synchronized void perform() {
156 if(performing) return;
159 Pair<Integer, Collection<JSONObject>> content = DocumentHistoryCollector.getContent(DocumentListener.this, document + "/" + ProxyChildVariable.CONTEXT_BEGIN + input + "/" + ProxyChildVariable.CONTEXT_END, revision);
160 revision = content.first;
161 updateDocument(content.second);
167 Runnable updater = new Runnable() {
172 // Pair<Integer, Collection<JSONObject>> content = DocumentHistoryCollector.getContent(DocumentListener.this, document + "/???" + input + "/???", revision);
173 // revision = content.first;
174 // updateDocument(content.second);
180 public void execute(Integer result) {
181 IThreadWorkQueue thread = thread();
182 if(thread.currentThreadAccess()) thread.syncExec(updater);
183 else thread.asyncExec(updater);
187 public boolean isDisposed() {
188 return support.isDisposed();
193 public void track(ListenerSupport support, String document, String input) {
195 new DocumentListener(support, document, input).perform();
197 // DocumentHistoryCollector.register(location, listener);
198 // listener.execute(-1);
202 WidgetData getRoot() {
203 return widgetData.get("root");
206 @SuppressWarnings("unchecked")
207 <T> T getRootWidget() {
208 return (T)getRoot().widget;
212 public CommandManager<?, ?> getCommandManager(JSONObject object) {
213 return commandMapping.getCommandManager(object.getType());