* omit the content and close the tag, in which case cached content should
* be used.
* </p>
+ * <p>
+ * This method may also add only a reference to the paintable and queue the
+ * paintable to be painted separately.
+ * </p>
+ * <p>
+ * Each paintable being painted should be closed by a matching
+ * {@link #endPaintable(Paintable)}.
+ * </p>
*
* @param paintable
* the paintable to start.
* @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.
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;
private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
+ // TODO combine with paint queue?
private final ArrayList<Paintable> dirtyPaintables = new ArrayList<Paintable>();
+ // queue used during painting to keep track of what still needs to be
+ // painted
+ private LinkedList<Paintable> paintQueue = new LinkedList<Paintable>();
+
private final HashMap<Paintable, String> paintableIdMap = new HashMap<Paintable, String>();
private final HashMap<String, Paintable> idPaintableMap = new HashMap<String, Paintable>();
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 {
dirtyPaintables.remove(p);
}
}
+ // TODO second list/stack for those whose state needs to be sent?
paintables = getDirtyVisibleComponents(root);
}
});
}
- 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<Paintable> 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<InvalidLayout> invalidComponentRelativeSizes = null;
if (paintables != null) {
- for (final Iterator<Paintable> 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) {
// 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)
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");
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<Paintable> paintablesWithModifiedState = new Stack<Paintable>();
+ 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;
private final Stack<JsonTag> openJsonTags;
+ // these match each other element-wise
+ private final Stack<Paintable> openPaintables;
+ private final Stack<String> openPaintableTags;
+
private final PrintWriter uidlBuffer;
private boolean closed = false;
private final Collection<Class<? extends Paintable>> usedPaintableTypes = new LinkedList<Class<? extends Paintable>>();
/**
- * Creates a new XMLPrintWriter, without automatic line flushing.
+ * Creates a new JsonPaintTarget.
*
- * @param variableMap
* @param manager
* @param outWriter
* A character-output stream.
// Initialize tag-writing
mOpenTags = new Stack<String>();
openJsonTags = new Stack<JsonTag>();
+
+ openPaintables = new Stack<Paintable>();
+ openPaintableTags = new Stack<String>();
+
cacheEnabled = cachingRequired;
}
* @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)
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;
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 {