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