From eba294b572985ba3447aca1593a35b67440d8ba1 Mon Sep 17 00:00:00 2001 From: Henri Sara Date: Tue, 14 Feb 2012 13:28:50 +0200 Subject: [PATCH] Set shared state for VPaintables before updateFromUIDL() (#8304). Also includes minor fixes related to shared state painting and decoding. Some data is currently duplicated in UIDL and shared state, and width and height are used from shared state except for a few cases that still parse them from UIDL explicitly. --- .../gwt/client/ApplicationConnection.java | 103 +++++++++++++----- .../terminal/gwt/client/VPaintable.java | 20 +++- .../gwt/client/communication/JsonDecoder.java | 2 +- .../client/ui/VAbstractPaintableWidget.java | 14 ++- .../server/AbstractCommunicationManager.java | 24 ++-- 5 files changed, 120 insertions(+), 43 deletions(-) diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index b41b4f1b3c..c8a676d6fa 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -993,30 +993,10 @@ public class ApplicationConnection { redirectTimer.schedule(1000 * sessionExpirationInterval); } - // TODO implement shared state handling - - // map from paintable id to its shared state instance - Map sharedStates = new HashMap(); - - // TODO Cache shared state (if any) - components might not exist - // TODO cleanup later: keep only used states - ValueMap states = json.getValueMap("state"); - JsArrayString keyArray = states.getKeyArray(); - for (int i = 0; i < keyArray.length(); i++) { - String paintableId = keyArray.get(i); - // TODO handle as a ValueMap or similar object and native - // JavaScript processing? - JavaScriptObject value = states - .getJavaScriptObject(paintableId); - // TODO implement with shared state subclasses - SharedState state = GWT.create(SharedState.class); - Map stateMap = (Map) JsonDecoder - .convertValue(new JSONArray(value), - getPaintableMap()); - state.setState(stateMap); - // TODO cache state to temporary stateMap - sharedStates.put(paintableId, state); - } + // three phases/loops: + // - changes: create paintables (if necessary) + // - state: set shared states + // - changes: call updateFromUIDL() for each paintable // Process changes JsArray changes = json.getJSValueMapArray("changes"); @@ -1026,6 +1006,52 @@ public class ApplicationConnection { componentCaptionSizeChanges.clear(); int length = changes.length(); + + // create paintables if necessary + for (int i = 0; i < length; i++) { + try { + final UIDL change = changes.get(i).cast(); + final UIDL uidl = change.getChildUIDL(0); + VPaintable paintable = paintableMap.getPaintable(uidl + .getId()); + if (null == paintable + && !uidl.getTag().equals( + configuration.getEncodedWindowTag())) { + // create, initialize and register the paintable + getPaintable(uidl); + } + } catch (final Throwable e) { + VConsole.error(e); + } + } + + // set states for all paintables mentioned in "state" + ValueMap states = json.getValueMap("state"); + JsArrayString keyArray = states.getKeyArray(); + for (int i = 0; i < keyArray.length(); i++) { + try { + String paintableId = keyArray.get(i); + VPaintable paintable = paintableMap + .getPaintable(paintableId); + if (null != paintable) { + // TODO handle as a ValueMap or similar object and + // native JavaScript processing? + JavaScriptObject value = states + .getJavaScriptObject(paintableId); + // TODO implement with shared state subclasses + SharedState state = GWT.create(SharedState.class); + Map stateMap = (Map) JsonDecoder + .convertValue(new JSONArray(value), + getPaintableMap()); + state.setState(stateMap); + paintable.updateState(state); + } + } catch (final Throwable e) { + VConsole.error(e); + } + } + + // update paintables for (int i = 0; i < length; i++) { try { final UIDL change = changes.get(i).cast(); @@ -1567,12 +1593,29 @@ public class ApplicationConnection { return result.toString(); } - public void updateComponentSize(VPaintableWidget paintable, UIDL uidl) { - String w = uidl.hasAttribute("width") ? uidl - .getStringAttribute("width") : ""; - - String h = uidl.hasAttribute("height") ? uidl - .getStringAttribute("height") : ""; + public void updateComponentSize(VPaintableWidget paintable) { + SharedState state = paintable.getState(); + String w = ""; + String h = ""; + if (null != state) { + // TODO move logging to VUIDLBrowser and VDebugConsole + VConsole.log("Paintable state for " + + getPaintableMap().getPid(paintable) + ": " + + String.valueOf(state.getState())); + if (state.getState().containsKey(ComponentState.STATE_WIDTH)) { + w = String.valueOf(state.getState().get( + ComponentState.STATE_WIDTH)); + } + if (state.getState().containsKey(ComponentState.STATE_HEIGHT)) { + h = String.valueOf(state.getState().get( + ComponentState.STATE_HEIGHT)); + } + } else { + // TODO move logging to VUIDLBrowser and VDebugConsole + VConsole.log("No state for paintable " + + getPaintableMap().getPid(paintable) + + " in ApplicationConnection.updateComponentSize()"); + } float relativeWidth = Util.parseRelativeSize(w); float relativeHeight = Util.parseRelativeSize(h); diff --git a/src/com/vaadin/terminal/gwt/client/VPaintable.java b/src/com/vaadin/terminal/gwt/client/VPaintable.java index 032058b8d5..666b620d82 100644 --- a/src/com/vaadin/terminal/gwt/client/VPaintable.java +++ b/src/com/vaadin/terminal/gwt/client/VPaintable.java @@ -3,6 +3,8 @@ */ package com.vaadin.terminal.gwt.client; +import com.vaadin.terminal.gwt.client.communication.SharedState; + /** * Interface implemented by all client side classes that can be communicate with * the server. Classes implementing this interface are initialized by the @@ -21,6 +23,20 @@ public interface VPaintable { */ public void updateFromUIDL(UIDL uidl, ApplicationConnection client); + /** + * Sets the shared state for the paintable. + * + * @param state + */ + public void updateState(SharedState state); + + /** + * Gets the current shared state of the paintable. + * + * @return state + */ + public SharedState getState(); + /** * Returns the id for this VPaintable. This must always be what has been set * using {@link #setId(String)}. @@ -73,7 +89,9 @@ public interface VPaintable { /** * - * Called once when the connection and id has been set + * Called once when the connection and id has been set. + * + * Note that the shared state is not yet available during init(). */ public void init(); diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java index 7ed212af49..e7369e1a8c 100644 --- a/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java +++ b/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java @@ -58,7 +58,7 @@ public class JsonDecoder { val = convertStringArray((JSONArray) value); break; case JsonEncoder.VTYPE_STRING: - val = value; + val = ((JSONString) value).stringValue(); break; case JsonEncoder.VTYPE_INTEGER: // TODO handle properly diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java b/src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java index 746bc99ba4..3cbe8da00f 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java @@ -13,6 +13,7 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.VPaintableMap; import com.vaadin.terminal.gwt.client.VPaintableWidget; import com.vaadin.terminal.gwt.client.VPaintableWidgetContainer; +import com.vaadin.terminal.gwt.client.communication.SharedState; public abstract class VAbstractPaintableWidget implements VPaintableWidget { @@ -27,6 +28,9 @@ public abstract class VAbstractPaintableWidget implements VPaintableWidget { private boolean enabled = true; private boolean visible = true; + // shared state from the server to the client + private SharedState state; + /** * Default constructor */ @@ -89,6 +93,14 @@ public abstract class VAbstractPaintableWidget implements VPaintableWidget { this.id = id; } + public void updateState(SharedState state) { + this.state = state; + } + + public SharedState getState() { + return state; + } + public VPaintableWidgetContainer getParent() { // FIXME: Hierarchy should be set by framework instead of looked up here VPaintableMap paintableMap = VPaintableMap.get(getConnection()); @@ -181,7 +193,7 @@ public abstract class VAbstractPaintableWidget implements VPaintableWidget { * taken into account */ - getConnection().updateComponentSize(this, uidl); + getConnection().updateComponentSize(this); } /** diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 7b75592442..85643e2b8a 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -37,7 +37,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.Stack; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -142,11 +141,12 @@ public abstract class AbstractCommunicationManager implements private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts"; - // TODO combine with paint queue? + // cannot combine with paint queue: + // this can contain dirty components from any Root private final ArrayList dirtyPaintables = new ArrayList(); // queue used during painting to keep track of what still needs to be - // painted + // painted within the Root being painted private LinkedList paintQueue = new LinkedList(); private final HashMap paintableIdMap = new HashMap(); @@ -747,7 +747,9 @@ public abstract class AbstractCommunicationManager implements // for internal use by JsonPaintTarget public void queuePaintable(Paintable paintable) { - paintQueue.push(paintable); + if (!paintQueue.contains(paintable)) { + paintQueue.add(paintable); + } } public void writeUidlResponce(boolean repaintAll, @@ -835,6 +837,8 @@ public abstract class AbstractCommunicationManager implements windowCache); } + LinkedList stateQueue = new LinkedList(); + if (paintables != null) { // clear and rebuild paint queue @@ -843,6 +847,8 @@ public abstract class AbstractCommunicationManager implements while (!paintQueue.isEmpty()) { final Paintable p = paintQueue.pop(); + // for now, all painted components may need a state refresh + stateQueue.push(p); // // TODO CLEAN // if (p instanceof Root) { @@ -891,7 +897,7 @@ public abstract class AbstractCommunicationManager implements paintTarget.close(); outWriter.print("], "); // close changes - if (paintables != null) { + if (!stateQueue.isEmpty()) { // paint shared state // for now, send the complete state of all modified and new @@ -904,11 +910,8 @@ public abstract class AbstractCommunicationManager implements // processing. JSONObject sharedStates = new JSONObject(); - Stack paintablesWithModifiedState = new Stack(); - paintablesWithModifiedState.addAll(paintables); - // TODO add all sub-components that were painted - while (!paintablesWithModifiedState.empty()) { - final Paintable p = paintablesWithModifiedState.pop(); + while (!stateQueue.isEmpty()) { + final Paintable p = stateQueue.pop(); String paintableId = getPaintableId(p); SharedState state = p.getState(); if (null != state) { @@ -1869,6 +1872,7 @@ public abstract class AbstractCommunicationManager implements final ArrayList resultset = new ArrayList( dirtyPaintables); + // TODO mostly unnecessary? // The following algorithm removes any components that would be painted // as a direct descendant of other components from the dirty components // list. The result is that each component should be painted exactly -- 2.39.5