From 815406969b267f0250339e53af4e69370375d90a Mon Sep 17 00:00:00 2001
From: Henri Sara
Date: Thu, 9 Feb 2012 11:07:40 +0200
Subject: [PATCH] Refactor paint target to use startPaintable() and
endPaintable().
Also includes initial steps towards support for a queue of components to
paint separately on the top level.
---
src/com/vaadin/terminal/PaintTarget.java | 36 +++----
.../server/AbstractCommunicationManager.java | 101 +++++++++++-------
.../terminal/gwt/server/JsonPaintTarget.java | 40 +++++--
src/com/vaadin/ui/AbstractComponent.java | 5 +-
4 files changed, 114 insertions(+), 68 deletions(-)
diff --git a/src/com/vaadin/terminal/PaintTarget.java b/src/com/vaadin/terminal/PaintTarget.java
index 68f68e2b51..78cc9e0af9 100644
--- a/src/com/vaadin/terminal/PaintTarget.java
+++ b/src/com/vaadin/terminal/PaintTarget.java
@@ -46,6 +46,14 @@ public interface PaintTarget extends Serializable {
* omit the content and close the tag, in which case cached content should
* be used.
*
+ *
+ * This method may also add only a reference to the paintable and queue the
+ * paintable to be painted separately.
+ *
+ *
+ * Each paintable being painted should be closed by a matching
+ * {@link #endPaintable(Paintable)}.
+ *
*
* @param paintable
* the paintable to start.
@@ -56,33 +64,25 @@ public interface PaintTarget extends Serializable {
* @throws PaintException
* if the paint operation failed.
* @see #startTag(String)
- * @since 3.1
+ * @since 7.0 (previously using startTag(Paintable, String))
*/
- public boolean startTag(Paintable paintable, String tag)
+ public boolean startPaintable(Paintable paintable, String tag)
throws PaintException;
/**
- * Paints a component reference as an attribute to current tag. This method
- * is meant to enable component interactions on client side. With reference
- * the client side component can communicate directly to other component.
+ * Prints paintable element end tag.
*
- * Note! This was experimental api and got replaced by
- * {@link #addAttribute(String, Paintable)} and
- * {@link #addVariable(VariableOwner, String, Paintable)}.
+ * Calls to {@link #startPaintable(Paintable, String)} should be matched by
+ * {@link #endPaintable(Paintable)}. If the parent tag is closed before
+ * every child tag is closed a PaintException is raised.
*
* @param paintable
- * the Paintable to reference
- * @param referenceName
+ * the paintable to close.
* @throws PaintException
- *
- * @since 5.2
- * @deprecated use {@link #addAttribute(String, Paintable)} or
- * {@link #addVariable(VariableOwner, String, Paintable)}
- * instead
+ * if the paint operation failed.
+ * @since 7.0 (previously using engTag(String))
*/
- @Deprecated
- public void paintReference(Paintable paintable, String referenceName)
- throws PaintException;
+ public void endPaintable(Paintable paintable) throws PaintException;
/**
* Prints element start tag.
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index 168d6543f9..4421001a3f 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -37,6 +37,7 @@ 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;
@@ -141,8 +142,13 @@ public abstract class AbstractCommunicationManager implements
private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
+ // TODO combine with paint queue?
private final ArrayList dirtyPaintables = new ArrayList();
+ // queue used during painting to keep track of what still needs to be
+ // painted
+ private LinkedList paintQueue = new LinkedList();
+
private final HashMap paintableIdMap = new HashMap();
private final HashMap idPaintableMap = new HashMap();
@@ -739,6 +745,11 @@ public abstract class AbstractCommunicationManager implements
return seckey;
}
+ // for internal use by JsonPaintTarget
+ public void queuePaintable(Paintable paintable) {
+ paintQueue.push(paintable);
+ }
+
public void writeUidlResponce(boolean repaintAll,
final PrintWriter outWriter, Root root, boolean analyzeLayouts)
throws PaintException {
@@ -777,6 +788,7 @@ public abstract class AbstractCommunicationManager implements
dirtyPaintables.remove(p);
}
}
+ // TODO second list/stack for those whose state needs to be sent?
paintables = getDirtyVisibleComponents(root);
}
@@ -809,37 +821,6 @@ public abstract class AbstractCommunicationManager implements
});
}
- if (paintables != null) {
- // paint shared state before changes - for now, send the complete
- // state of all modified and new components
-
- // TODO problem: some components will only be created and registered
- // below in the paint phase
-
- JSONObject sharedStates = new JSONObject();
- for (final Iterator i = paintables.iterator(); i
- .hasNext();) {
- final Paintable p = i.next();
- String paintableId = getPaintableId(p);
- SharedState state = p.getState();
- if (null != state) {
- // encode and send shared state
- try {
- JSONArray stateJsonArray = JsonCodec
- .encode(state, this);
- sharedStates.put(paintableId, stateJsonArray);
- } catch (JSONException e) {
- throw new PaintException(
- "Failed to serialize shared state for paintable "
- + paintableId + ": " + e.getMessage());
- }
- }
- }
- outWriter.print("\"state\":");
- outWriter.append(sharedStates.toString());
- outWriter.print(", "); // close changes
- }
-
outWriter.print("\"changes\":[");
List invalidComponentRelativeSizes = null;
@@ -856,9 +837,12 @@ public abstract class AbstractCommunicationManager implements
if (paintables != null) {
- for (final Iterator i = paintables.iterator(); i
- .hasNext();) {
- final Paintable p = i.next();
+ // clear and rebuild paint queue
+ paintQueue.clear();
+ paintQueue.addAll(paintables);
+
+ while (!paintQueue.isEmpty()) {
+ final Paintable p = paintQueue.pop();
// // TODO CLEAN
// if (p instanceof Root) {
@@ -867,13 +851,6 @@ public abstract class AbstractCommunicationManager implements
// r.setTerminal(application.getRoot().getTerminal());
// }
// }
- /*
- * This does not seem to happen in tk5, but remember this case:
- * else if (p instanceof Component) { if (((Component)
- * p).getParent() == null || ((Component) p).getApplication() ==
- * null) { // Component requested repaint, but is no // longer
- * attached: skip paintablePainted(p); continue; } }
- */
// TODO we may still get changes that have been
// rendered already (changes with only cached flag)
@@ -883,6 +860,8 @@ public abstract class AbstractCommunicationManager implements
final String pid = getPaintableId(p);
paintTarget.addAttribute("pid", pid);
+ // TODO this should paint subcomponents as references and
+ // defer painting their contents to another top-level change
p.paint(paintTarget);
paintTarget.endTag("change");
@@ -911,6 +890,46 @@ public abstract class AbstractCommunicationManager implements
paintTarget.close();
outWriter.print("], "); // close changes
+ if (paintables != null) {
+ // paint shared state
+
+ // for now, send the complete state of all modified and new
+ // components
+
+ // Ideally, all this would be sent before "changes", but that causes
+ // complications with legacy components that create sub-components
+ // in their paint phase. Nevertheless, this will be processed on the
+ // client after component creation but before legacy UIDL
+ // 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();
+ String paintableId = getPaintableId(p);
+ SharedState state = p.getState();
+ if (null != state) {
+ // encode and send shared state
+ try {
+ JSONArray stateJsonArray = JsonCodec
+ .encode(state, this);
+ sharedStates.put(paintableId, stateJsonArray);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to serialize shared state for paintable "
+ + paintableId + ": " + e.getMessage());
+ }
+ }
+ }
+ outWriter.print("\"state\":");
+ outWriter.append(sharedStates.toString());
+ outWriter.print(", "); // close changes
+ }
+
+ // TODO send server to client RPC calls in call order here
+
outWriter.print("\"meta\" : {");
boolean metaOpen = false;
diff --git a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
index 91c15df5b2..3f68577f8c 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
@@ -64,6 +64,10 @@ public class JsonPaintTarget implements PaintTarget {
private final Stack openJsonTags;
+ // these match each other element-wise
+ private final Stack openPaintables;
+ private final Stack openPaintableTags;
+
private final PrintWriter uidlBuffer;
private boolean closed = false;
@@ -89,9 +93,8 @@ public class JsonPaintTarget implements PaintTarget {
private final Collection> usedPaintableTypes = new LinkedList>();
/**
- * Creates a new XMLPrintWriter, without automatic line flushing.
+ * Creates a new JsonPaintTarget.
*
- * @param variableMap
* @param manager
* @param outWriter
* A character-output stream.
@@ -113,6 +116,10 @@ public class JsonPaintTarget implements PaintTarget {
// Initialize tag-writing
mOpenTags = new Stack();
openJsonTags = new Stack();
+
+ openPaintables = new Stack();
+ openPaintableTags = new Stack();
+
cacheEnabled = cachingRequired;
}
@@ -673,7 +680,7 @@ public class JsonPaintTarget implements PaintTarget {
* @see com.vaadin.terminal.PaintTarget#startTag(com.vaadin.terminal
* .Paintable, java.lang.String)
*/
- public boolean startTag(Paintable paintable, String tagName)
+ public boolean startPaintable(Paintable paintable, String tagName)
throws PaintException {
startTag(tagName, true);
final boolean isPreviouslyPainted = manager.hasPaintableId(paintable)
@@ -682,7 +689,19 @@ public class JsonPaintTarget implements PaintTarget {
final String id = manager.getPaintableId(paintable);
paintable.addListener(manager);
addAttribute("id", id);
+
+ // TODO if to queue if already painting a paintable
+ // if (openPaintables.isEmpty()) {
+ openPaintables.push(paintable);
+ openPaintableTags.push(tagName);
+
paintedComponents.add(paintable);
+ // } else {
+ // // notify manager: add to paint queue instead of painting now
+ // manager.queuePaintable(paintable);
+ // // TODO return suitable value to defer painting and close tag if a
+ // // paintable tag is already open
+ // }
if (paintable instanceof CustomLayout) {
customLayoutArgumentsOpen = true;
@@ -691,10 +710,17 @@ public class JsonPaintTarget implements PaintTarget {
return cacheEnabled && isPreviouslyPainted;
}
- @Deprecated
- public void paintReference(Paintable paintable, String referenceName)
- throws PaintException {
- addAttribute(referenceName, paintable);
+ public void endPaintable(Paintable paintable) throws PaintException {
+ Paintable openPaintable = openPaintables.peek();
+ if (paintable != openPaintable) {
+ throw new PaintException("Invalid UIDL: closing wrong paintable: '"
+ + getPaintIdentifier(paintable) + "' expected: '"
+ + getPaintIdentifier(openPaintable) + "'.");
+ }
+ // remove paintable from the stack
+ openPaintables.pop();
+ String openTag = openPaintableTags.pop();
+ endTag(openTag);
}
public String getPaintIdentifier(Paintable paintable) throws PaintException {
diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java
index e27670530b..fa46441d06 100644
--- a/src/com/vaadin/ui/AbstractComponent.java
+++ b/src/com/vaadin/ui/AbstractComponent.java
@@ -753,7 +753,8 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
public void paint(PaintTarget target) throws PaintException {
final String tag = target.getTag(this);
- if (!target.startTag(this, tag) || repaintRequestListenersNotified) {
+ if (!target.startPaintable(this, tag)
+ || repaintRequestListenersNotified) {
// Paint the contents of the component
@@ -813,7 +814,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
// Contents have not changed, only cached presentation can be used
target.addAttribute("cached", true);
}
- target.endTag(tag);
+ target.endPaintable(this);
repaintRequestListenersNotified = false;
}
--
2.39.5