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 return mapping.getWidget(object.getType());
35 public WidgetData getWidget(String id) {
36 return widgetData.get(id);
39 abstract public IThreadWorkQueue thread();
41 protected void updateDocument(Collection<JSONObject> objects) {
43 ArrayList<WidgetData> updatedData = new ArrayList<WidgetData>();
45 for (JSONObject object : objects) {
46 WidgetData datum = widgetData.get(object.getId());
48 datum = new WidgetData(this, null, object);
49 widgetData.put(object.getId(), datum);
51 // This could replace changed information instead of replacing the whole object
52 datum.object = object;
54 updatedData.add(datum);
58 for(WidgetData datum : updatedData) {
59 createElementIfNecessary(datum);
62 HashSet<WidgetData> updatedParents = new HashSet<WidgetData>();
64 // Update parent hierarchy
65 for(WidgetData datum : updatedData) {
66 updateChildmaps(datum, updatedParents);
69 updateTree(updatedParents);
72 for(WidgetData datum : updatedData) {
73 datum.updateProperties();
74 datum.updateCommands();
79 private void createElementIfNecessary(WidgetData data) {
80 if(data.widget == null) {
81 data.widget = data.createElement();
85 private void updateChildmaps(WidgetData data, HashSet<WidgetData> updates) {
88 if(data.object == null) return;
90 // System.err.println("p:" + data.object);
91 // Object o = data.object.getParent();
93 WidgetData parent = widgetData.get(data.object.getParent());
95 // System.out.println("parent:" + parent);
97 if(parent == null) return;
99 String parentOrd = data.object.getParentOrd();
101 WidgetData existing = parent.childmap.get(parentOrd);
102 if(!data.equals(existing)) {
104 parent.childmap.put(parentOrd, data);
109 protected void updateTree(HashSet<WidgetData> updates) {
111 if(updates.isEmpty()) return;
113 ArrayList<WidgetData> updateList = new ArrayList<WidgetData>(updates);
114 Collections.sort(updateList, new Comparator<WidgetData>() {
116 private boolean isParent(WidgetData data, WidgetData possibleParent) {
117 WidgetData parent = widgetData.get(data.object.getParent());
118 if(parent == null) return false;
119 else if(parent == possibleParent) return true;
120 else return isParent(parent, possibleParent);
124 public int compare(WidgetData arg0, WidgetData arg1) {
125 if(arg0 == arg1) return 0;
126 else if( isParent(arg0, arg1) ) return 1;
127 else if( isParent(arg1, arg0) ) return -1;
133 for(WidgetData d : updateList) d.updateChildren();
137 public class DocumentListener extends ListenerAdapter<Integer> {
141 final private ListenerSupport support;
142 final private String document;
143 final private String input;
144 private boolean performing = false;
146 public DocumentListener(ListenerSupport support, String document, String input) {
147 this.support = support;
148 this.document = document;
152 public synchronized void perform() {
153 if(performing) return;
156 Pair<Integer, Collection<JSONObject>> content = DocumentHistoryCollector.getContent(DocumentListener.this, document + "/" + ProxyChildVariable.CONTEXT_BEGIN + input + "/" + ProxyChildVariable.CONTEXT_END, revision);
157 revision = content.first;
158 updateDocument(content.second);
164 Runnable updater = new Runnable() {
169 // Pair<Integer, Collection<JSONObject>> content = DocumentHistoryCollector.getContent(DocumentListener.this, document + "/???" + input + "/???", revision);
170 // revision = content.first;
171 // updateDocument(content.second);
177 public void execute(Integer result) {
178 IThreadWorkQueue thread = thread();
179 if(thread.currentThreadAccess()) thread.syncExec(updater);
180 else thread.asyncExec(updater);
184 public boolean isDisposed() {
185 return support.isDisposed();
190 public void track(ListenerSupport support, String document, String input) {
192 new DocumentListener(support, document, input).perform();
194 // DocumentHistoryCollector.register(location, listener);
195 // listener.execute(-1);
199 WidgetData getRoot() {
200 return widgetData.get("root");
203 @SuppressWarnings("unchecked")
204 <T> T getRootWidget() {
205 return (T)getRoot().widget;
209 public CommandManager<?, ?> getCommandManager(JSONObject object) {
210 return commandMapping.getCommandManager(object.getType());