]> source.dussan.org Git - vaadin-framework.git/commitdiff
Moved Connector -> Connector Id mapping to AbstractComponent
authorArtur Signell <artur@vaadin.com>
Fri, 16 Mar 2012 17:41:46 +0000 (19:41 +0200)
committerArtur Signell <artur@vaadin.com>
Wed, 21 Mar 2012 13:27:29 +0000 (15:27 +0200)
Moved Connector Id -> Connector mapping to Application
Moved dirty connector tracking to Root
Removed adding of

19 files changed:
src/com/vaadin/Application.java
src/com/vaadin/terminal/Paintable.java
src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java
src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
src/com/vaadin/terminal/gwt/server/ClientConnector.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
src/com/vaadin/terminal/gwt/server/CommunicationManager.java
src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
src/com/vaadin/terminal/gwt/server/DragAndDropService.java
src/com/vaadin/terminal/gwt/server/JsonCodec.java
src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
src/com/vaadin/ui/AbstractComponent.java
src/com/vaadin/ui/Component.java
src/com/vaadin/ui/DirtyConnectorTracker.java [new file with mode: 0644]
src/com/vaadin/ui/Root.java

index e426ea30857b37bba90c9802378c101b4f1ba2ce..fdb7de0454071aff6267966708ce90230e103d78 100644 (file)
@@ -18,6 +18,7 @@ import java.util.EventObject;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Map;
@@ -47,11 +48,13 @@ import com.vaadin.terminal.WrappedRequest;
 import com.vaadin.terminal.WrappedRequest.BrowserDetails;
 import com.vaadin.terminal.WrappedResponse;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
 import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
 import com.vaadin.terminal.gwt.server.WebApplicationContext;
 import com.vaadin.ui.AbstractComponent;
 import com.vaadin.ui.AbstractField;
+import com.vaadin.ui.Component;
 import com.vaadin.ui.Root;
 import com.vaadin.ui.Table;
 import com.vaadin.ui.Window;
@@ -2266,4 +2269,67 @@ public class Application implements Terminal.ErrorListener, Serializable {
     public Collection<Root> getRoots() {
         return Collections.unmodifiableCollection(roots.values());
     }
+
+    private final HashMap<String, Connector> connectorIdToConnector = new HashMap<String, Connector>();
+
+    private int connectorIdSequence = 0;
+
+    /**
+     * Generate an id for the given Connector. Connectors must not call this
+     * method more than once, the first time they need an id.
+     * 
+     * @param connector
+     *            A connector that has not yet been assigned an id.
+     * @return A new id for the connector
+     */
+    public String createConnectorId(Connector connector) {
+        // String connectorId = "C_" + connectorIdSequence++;
+        String connectorId = String.valueOf(connectorIdSequence++);
+        Connector oldReference = connectorIdToConnector.put(connectorId,
+                connector);
+        if (oldReference != null) {
+            throw new RuntimeException(
+                    "An error occured while generating connector ids. A connector with id "
+                            + connectorId + " was already found!");
+        }
+        return connectorId;
+    }
+
+    /**
+     * Gets a connector by its id.
+     * 
+     * @param connectorId
+     *            The connector id to look for
+     * @return The connector with the given id or null if no connector has the
+     *         given id
+     */
+    public Connector getConnector(String connectorId) {
+        return connectorIdToConnector.get(connectorId);
+    }
+
+    /**
+     * Cleans the connector map from all connectors that are no longer attached
+     * to the application. This should only be called by the framework.
+     */
+    public void cleanConnectorMap() {
+        // remove detached components from paintableIdMap so they
+        // can be GC'ed
+        Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
+
+        while (iterator.hasNext()) {
+            String connectorId = iterator.next();
+            Connector connector = connectorIdToConnector.get(connectorId);
+            if (connector instanceof Component) {
+                Component component = (Component) connector;
+                if (component.getApplication() != this) {
+                    // If component is no longer part of this application,
+                    // remove it from the map. If it is re-attached to the
+                    // application at some point it will be re-added to this
+                    // collection when sent to the client.
+                    iterator.remove();
+                }
+            }
+        }
+
+    }
 }
index 435b00a0e4bf7e5c8a95bebab9195c9ee99b3567..d043cb26066765ed5180e31975c1ffdd2092e20d 100644 (file)
@@ -6,10 +6,6 @@ package com.vaadin.terminal;
 
 import java.io.Serializable;
 import java.util.EventObject;
-import java.util.List;
-
-import com.vaadin.terminal.gwt.client.communication.SharedState;
-import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
 
 /**
  * Interface implemented by all classes that can be painted. Classes
@@ -43,30 +39,6 @@ public interface Paintable extends java.util.EventListener, Serializable {
      */
     public void paint(PaintTarget target) throws PaintException;
 
-    /**
-     * Returns the current shared state bean for the paintable. The state (or
-     * changes to it) is communicated from the server to the client when
-     * components are painted.
-     * 
-     * Subclasses can use a more specific return type for this method.
-     * 
-     * @return shared state instance or null
-     * 
-     * @since 7.0
-     */
-    public SharedState getState();
-
-    /**
-     * Returns the list of pending server to client RPC calls and clears the
-     * list.
-     * 
-     * @return unmodifiable ordered list of pending server to client method
-     *         calls (not null)
-     * 
-     * @since 7.0
-     */
-    public List<ClientMethodInvocation> retrievePendingRpcCalls();
-
     /**
      * Requests that the paintable should be repainted as soon as possible.
      */
index e034ae563d05d1818c5841fd37776323900b88f0..4bc9431d49da521c9914695030c299af9896cf48 100644 (file)
@@ -82,7 +82,7 @@ public class JsonDecoder {
         } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) {
             // TODO handle properly
             val = Boolean.valueOf(String.valueOf(value));
-        } else if (JsonEncoder.VTYPE_PAINTABLE.equals(variableType)) {
+        } else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) {
             val = idMapper.getConnector(((JSONString) value).stringValue());
         } else {
             // object, class name as type
index c704f36871030124d2b0ea6f52ee61019e368583..cc8b36dfa4a37e7ae9fb744d7a55eb04746542ff 100644 (file)
@@ -14,8 +14,8 @@ import com.google.gwt.json.client.JSONObject;
 import com.google.gwt.json.client.JSONString;
 import com.google.gwt.json.client.JSONValue;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.terminal.gwt.client.ConnectorMap;
-import com.vaadin.terminal.gwt.client.ServerConnector;
 
 /**
  * Encoder for converting RPC parameters and other values to JSON for transfer
@@ -30,7 +30,7 @@ import com.vaadin.terminal.gwt.client.ServerConnector;
  */
 public class JsonEncoder {
 
-    public static final String VTYPE_PAINTABLE = "p";
+    public static final String VTYPE_CONNECTOR = "c";
     public static final String VTYPE_BOOLEAN = "b";
     public static final String VTYPE_DOUBLE = "d";
     public static final String VTYPE_FLOAT = "f";
@@ -38,7 +38,7 @@ public class JsonEncoder {
     public static final String VTYPE_INTEGER = "i";
     public static final String VTYPE_STRING = "s";
     public static final String VTYPE_ARRAY = "a";
-    public static final String VTYPE_STRINGARRAY = "c";
+    public static final String VTYPE_STRINGARRAY = "S";
     public static final String VTYPE_MAP = "m";
     public static final String VTYPE_LIST = "L";
     public static final String VTYPE_NULL = "n";
@@ -88,10 +88,10 @@ public class JsonEncoder {
                 jsonMap.put(mapKey, encode(mapValue, connectorMap, connection));
             }
             return combineTypeAndValue(VTYPE_MAP, jsonMap);
-        } else if (value instanceof ServerConnector) {
-            ServerConnector paintable = (ServerConnector) value;
-            return combineTypeAndValue(VTYPE_PAINTABLE, new JSONString(
-                    connectorMap.getConnectorId(paintable)));
+        } else if (value instanceof Connector) {
+            Connector connector = (Connector) value;
+            return combineTypeAndValue(VTYPE_CONNECTOR, new JSONString(
+                    connector.getConnectorId()));
         } else {
             String transportType = getTransportType(value);
             if (transportType != null) {
@@ -123,8 +123,8 @@ public class JsonEncoder {
             return VTYPE_NULL;
         } else if (value instanceof String) {
             return VTYPE_STRING;
-        } else if (value instanceof ServerConnector) {
-            return VTYPE_PAINTABLE;
+        } else if (value instanceof Connector) {
+            return VTYPE_CONNECTOR;
         } else if (value instanceof Boolean) {
             return VTYPE_BOOLEAN;
         } else if (value instanceof Integer) {
index 4955eea7c6e8117eada8b8d9fc501219cc39dd5d..c96f9558706c4aced9d87aa6fb8b3048576ce61b 100644 (file)
@@ -223,7 +223,7 @@ public class VDragAndDropManager {
         ENTER, LEAVE, OVER, DROP
     }
 
-    private static final String DD_SERVICE = "DD";
+    public static final String DD_SERVICE = "DD";
 
     private static VDragAndDropManager instance;
     private HandlerRegistration handlerRegistration;
index b28cb361b0598c57f1ecc21b6dd1b904e135ebee..633c393913e72b879eb620ffbb68956f508de5b0 100644 (file)
@@ -472,7 +472,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
 
             /* Handle the request */
             if (requestType == RequestType.FILE_UPLOAD) {
-                applicationManager.handleFileUpload(request, response);
+                applicationManager.handleFileUpload(application, request,
+                        response);
                 return;
             } else if (requestType == RequestType.UIDL) {
                 // Handles AJAX UIDL requests
index 6a5bcce7d71685cd379d9ac8481c5e089d2c57ab..37b050921855f5ab9df9dbdb4ab38d9a2ccb7f78 100644 (file)
@@ -69,9 +69,11 @@ import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout;
 import com.vaadin.ui.AbstractComponent;
 import com.vaadin.ui.AbstractField;
 import com.vaadin.ui.Component;
+import com.vaadin.ui.DirtyConnectorTracker;
 import com.vaadin.ui.HasComponents;
 import com.vaadin.ui.Panel;
 import com.vaadin.ui.Root;
+import com.vaadin.ui.Window;
 
 /**
  * This is a common base class for the server-side implementations of the
@@ -91,7 +93,7 @@ import com.vaadin.ui.Root;
  */
 @SuppressWarnings("serial")
 public abstract class AbstractCommunicationManager implements
-        Paintable.RepaintRequestListener, PaintableIdMapper, Serializable {
+        Paintable.RepaintRequestListener, Serializable {
 
     private static final String DASHDASH = "--";
 
@@ -142,15 +144,16 @@ public abstract class AbstractCommunicationManager implements
 
     // cannot combine with paint queue:
     // this can contain dirty components from any Root
-    private final ArrayList<Paintable> dirtyPaintables = new ArrayList<Paintable>();
+    // TODO Remove, it is now in Root
+    // private final ArrayList<Paintable> dirtyPaintables = new
+    // ArrayList<Paintable>();
 
     // queue used during painting to keep track of what still needs to be
     // painted within the Root being painted
-    private LinkedList<Paintable> paintQueue = new LinkedList<Paintable>();
+    // private LinkedList<Connector> paintQueue = new LinkedList<Connector>();
 
-    private final HashMap<Paintable, String> paintableIdMap = new HashMap<Paintable, String>();
-
-    private final HashMap<String, Paintable> idPaintableMap = new HashMap<String, Paintable>();
+    // private final HashMap<String, Paintable> idPaintableMap = new
+    // HashMap<String, Paintable>();
 
     private int idSequence = 0;
 
@@ -168,6 +171,8 @@ public abstract class AbstractCommunicationManager implements
 
     private int maxInactiveInterval;
 
+    private Connector highlightedConnector;
+
     /**
      * TODO New constructor - document me!
      * 
@@ -193,8 +198,6 @@ public abstract class AbstractCommunicationManager implements
 
     private static final String GET_PARAM_HIGHLIGHT_COMPONENT = "highlightComponent";
 
-    private Paintable highLightedPaintable;
-
     private static String readLine(InputStream stream) throws IOException {
         ByteArrayOutputStream bout = new ByteArrayOutputStream();
         int readByte = stream.read();
@@ -220,7 +223,7 @@ public abstract class AbstractCommunicationManager implements
      */
     protected void doHandleSimpleMultipartFileUpload(WrappedRequest request,
             WrappedResponse response, StreamVariable streamVariable,
-            String variableName, VariableOwner owner, String boundary)
+            String variableName, Connector owner, String boundary)
             throws IOException {
         // multipart parsing, supports only one file for request, but that is
         // fine for our current terminal
@@ -319,7 +322,7 @@ public abstract class AbstractCommunicationManager implements
      */
     protected void doHandleXhrFilePost(WrappedRequest request,
             WrappedResponse response, StreamVariable streamVariable,
-            String variableName, VariableOwner owner, int contentLength)
+            String variableName, Connector owner, int contentLength)
             throws IOException {
 
         // These are unknown in filexhr ATM, maybe add to Accept header that
@@ -536,8 +539,8 @@ public abstract class AbstractCommunicationManager implements
             if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) {
                 String pid = request
                         .getParameter(GET_PARAM_HIGHLIGHT_COMPONENT);
-                highLightedPaintable = idPaintableMap.get(pid);
-                highlightPaintable(highLightedPaintable);
+                highlightedConnector = root.getApplication().getConnector(pid);
+                highlightConnector(highlightedConnector);
             }
         }
 
@@ -597,22 +600,35 @@ public abstract class AbstractCommunicationManager implements
 
             paintAfterVariableChanges(request, response, callback, repaintAll,
                     outWriter, root, analyzeLayouts);
-
+            postPaint(root);
         }
 
         outWriter.close();
         requestThemeName = null;
     }
 
-    protected void highlightPaintable(Paintable highLightedPaintable2) {
+    /**
+     * Method called after the paint phase while still being synchronized on the
+     * application
+     * 
+     * @param root
+     * 
+     */
+    protected void postPaint(Root root) {
+        // Remove connectors that have been detached from the application during
+        // handling of the request
+        root.getApplication().cleanConnectorMap();
+    }
+
+    protected void highlightConnector(Connector highlightedConnector) {
         StringBuilder sb = new StringBuilder();
         sb.append("*** Debug details of a component:  *** \n");
         sb.append("Type: ");
-        sb.append(highLightedPaintable2.getClass().getName());
-        if (highLightedPaintable2 instanceof AbstractComponent) {
-            AbstractComponent component = (AbstractComponent) highLightedPaintable2;
+        sb.append(highlightedConnector.getClass().getName());
+        if (highlightedConnector instanceof AbstractComponent) {
+            AbstractComponent component = (AbstractComponent) highlightedConnector;
             sb.append("\nId:");
-            sb.append(paintableIdMap.get(component));
+            sb.append(highlightedConnector.getConnectorId());
             if (component.getCaption() != null) {
                 sb.append("\nCaption:");
                 sb.append(component.getCaption());
@@ -681,10 +697,6 @@ public abstract class AbstractCommunicationManager implements
             final PrintWriter outWriter, Root root, boolean analyzeLayouts)
             throws PaintException, IOException {
 
-        if (repaintAll) {
-            makeAllPaintablesDirty(root);
-        }
-
         // Removes application if it has stopped during variable changes
         if (!application.isRunning()) {
             endApplication(request, response, application);
@@ -746,126 +758,75 @@ public abstract class AbstractCommunicationManager implements
 
     // for internal use by JsonPaintTarget
     public void queuePaintable(Paintable paintable) {
-        if (!paintQueue.contains(paintable)) {
-            paintQueue.add(paintable);
-        }
+        // if (!paintQueue.contains(paintable)) {
+        // paintQueue.add((Connector) paintable);
+        // }
     }
 
     public void writeUidlResponse(boolean repaintAll,
             final PrintWriter outWriter, Root root, boolean analyzeLayouts)
             throws PaintException {
-        ArrayList<Paintable> paintables = null;
-
+        ArrayList<Connector> dirtyVisibleConnectors = new ArrayList<Connector>();
+        Application application = root.getApplication();
         // Paints components
+        DirtyConnectorTracker rootConnectorTracker = root
+                .getDirtyConnectorTracker();
+        System.out.println("* Creating response to client");
         if (repaintAll) {
-            paintables = new ArrayList<Paintable>();
-            paintables.add(root);
+            System.out.println("Full repaint");
+
+            getClientCache(root).clear();
+
+            rootConnectorTracker.markAllComponentsDirty();
+
+            dirtyVisibleConnectors.addAll(rootConnectorTracker
+                    .getDirtyComponents());
 
             // Reset sent locales
             locales = null;
             requireLocale(application.getLocale().toString());
 
         } else {
-            // remove detached components from paintableIdMap so they
-            // can be GC'ed
-            /*
-             * TODO figure out if we could move this beyond the painting phase,
-             * "respond as fast as possible, then do the cleanup". Beware of
-             * painting the dirty detached components.
-             */
-            for (Iterator<Paintable> it = paintableIdMap.keySet().iterator(); it
-                    .hasNext();) {
-                Component p = (Component) it.next();
-                if (p.getApplication() == null) {
-                    unregisterPaintable(p);
-                    // Take into account that some other component may have
-                    // reused p's ID by now (this can happen when manually
-                    // assigning IDs with setDebugId().) See #8090.
-                    String pid = paintableIdMap.get(p);
-                    if (idPaintableMap.get(pid) == p) {
-                        idPaintableMap.remove(pid);
-                    }
-                    it.remove();
-                    dirtyPaintables.remove(p);
-                }
-            }
             // TODO second list/stack for those whose state needs to be sent?
-            paintables = getDirtyVisibleComponents(root);
+            dirtyVisibleConnectors
+                    .addAll(getDirtyVisibleComponents(rootConnectorTracker));
         }
 
+        System.out.println("* Found " + dirtyVisibleConnectors.size()
+                + " dirty connectors to paint");
+        rootConnectorTracker.markAllComponentsClean();
+
         outWriter.print("\"changes\":[");
 
         List<InvalidLayout> invalidComponentRelativeSizes = null;
 
         JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter,
                 !repaintAll);
-        ClientCache clientCache = getClientCache(root);
 
-        // TODO These seem unnecessary and could be removed/replaced by looping
-        // through paintQueue without removing paintables from it
-        LinkedList<Paintable> stateQueue = new LinkedList<Paintable>();
-        LinkedList<Paintable> rpcPendingQueue = new LinkedList<Paintable>();
-        LinkedList<Paintable> hierarchyPendingQueue = new LinkedList<Paintable>();
-        LinkedList<Paintable> connectorTypeQueue = new LinkedList<Paintable>();
-
-        if (paintables != null) {
-
-            // clear and rebuild paint queue
-            paintQueue.clear();
-            paintQueue.addAll(paintables);
-
-            while (!paintQueue.isEmpty()) {
-                final Paintable p = paintQueue.removeFirst();
-                // for now, all painted components may need a state refresh
-                stateQueue.push(p);
-                // TODO This should be optimized. The type only needs to be sent
-                // once for each connector id + on refresh
-                connectorTypeQueue.push(p);
-                // also a hierarchy update
-                hierarchyPendingQueue.push(p);
-                // ... and RPC calls to be sent
-                rpcPendingQueue.push(p);
-
-                // // TODO CLEAN
-                // if (p instanceof Root) {
-                // final Root r = (Root) p;
-                // if (r.getTerminal() == null) {
-                // r.setTerminal(application.getRoot().getTerminal());
-                // }
-                // }
-
-                // TODO we may still get changes that have been
-                // rendered already (changes with only cached flag)
-                if (paintTarget.needsToBePainted(p)) {
-                    paintTarget.startTag("change");
-                    final String pid = getPaintableId(p);
-                    paintTarget.addAttribute("pid", pid);
-
-                    // paints subcomponents as references (via
-                    // JsonPaintTarget.startPaintable()) and defers painting
-                    // their contents to another top-level change (via
-                    // queuePaintable())
-                    p.paint(paintTarget);
-
-                    paintTarget.endTag("change");
-                }
-                paintablePainted(p);
+        for (Connector connector : dirtyVisibleConnectors) {
+            if (connector instanceof Paintable) {
+                Paintable p = (Paintable) connector;
+                paintTarget.startTag("change");
+                final String pid = connector.getConnectorId();
+                paintTarget.addAttribute("pid", pid);
+                p.paint(paintTarget);
+                paintTarget.endTag("change");
+            }
+        }
 
-                if (analyzeLayouts) {
-                    Root w = (Root) p;
+        if (analyzeLayouts) {
+            // TODO Check if this works
+            invalidComponentRelativeSizes = ComponentSizeValidator
+                    .validateComponentRelativeSizes(root.getContent(), null,
+                            null);
+
+            // Also check any existing subwindows
+            if (root.getWindows() != null) {
+                for (Window subWindow : root.getWindows()) {
                     invalidComponentRelativeSizes = ComponentSizeValidator
-                            .validateComponentRelativeSizes(w.getContent(),
-                                    null, null);
-
-                    // // Also check any existing subwindows
-                    // if (w.getChildWindows() != null) {
-                    // for (Window subWindow : w.getChildWindows()) {
-                    // invalidComponentRelativeSizes = ComponentSizeValidator
-                    // .validateComponentRelativeSizes(
-                    // subWindow.getContent(),
-                    // invalidComponentRelativeSizes, null);
-                    // }
-                    // }
+                            .validateComponentRelativeSizes(
+                                    subWindow.getContent(),
+                                    invalidComponentRelativeSizes, null);
                 }
             }
         }
@@ -873,136 +834,131 @@ public abstract class AbstractCommunicationManager implements
         paintTarget.close();
         outWriter.print("], "); // close changes
 
-        if (!stateQueue.isEmpty()) {
-            // send shared state to client
-
-            // 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();
-            while (!stateQueue.isEmpty()) {
-                final Paintable p = stateQueue.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 states
-        }
-        if (!connectorTypeQueue.isEmpty()) {
-            JSONObject connectorTypes = new JSONObject();
-            while (!connectorTypeQueue.isEmpty()) {
-                final Paintable p = connectorTypeQueue.pop();
-                String paintableId = getPaintableId(p);
-                String connectorType = paintTarget.getTag(p);
+        // send shared state to client
+
+        // 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();
+        for (Connector connector : dirtyVisibleConnectors) {
+            SharedState state = connector.getState();
+            if (null != state) {
+                // encode and send shared state
                 try {
-                    connectorTypes.put(paintableId, connectorType);
+                    JSONArray stateJsonArray = JsonCodec.encode(state,
+                            application);
+                    sharedStates
+                            .put(connector.getConnectorId(), stateJsonArray);
                 } catch (JSONException e) {
                     throw new PaintException(
-                            "Failed to send connector type for paintable "
-                                    + paintableId + ": " + e.getMessage());
-                }
-            }
-            outWriter.print("\"types\":");
-            outWriter.append(connectorTypes.toString());
-            outWriter.print(", "); // close states
-        }
-        if (!hierarchyPendingQueue.isEmpty()) {
-            // Send update hierarchy information to the client.
-
-            // This could be optimized aswell to send only info if hierarchy has
-            // actually changed. Much like with the shared state. Note though
-            // that an empty hierarchy is information aswell (e.g. change from 1
-            // child to 0 children)
-
-            outWriter.print("\"hierarchy\":");
-
-            JSONObject hierarchyInfo = new JSONObject();
-            for (Paintable p : hierarchyPendingQueue) {
-                if (p instanceof HasComponents) {
-                    HasComponents parent = (HasComponents) p;
-                    String parentConnectorId = paintableIdMap.get(parent);
-                    JSONArray children = new JSONArray();
-
-                    for (Component child : getChildComponents(parent)) {
-                        if (isVisible(child)) {
-                            String childConnectorId = getPaintableId(child);
-                            children.put(childConnectorId);
-                        }
-                    }
-                    try {
-                        hierarchyInfo.put(parentConnectorId, children);
-                    } catch (JSONException e) {
-                        throw new PaintException(
-                                "Failed to send hierarchy information about "
-                                        + parentConnectorId
-                                        + " to the client: " + e.getMessage());
-                    }
+                            "Failed to serialize shared state for connector "
+                                    + connector.getConnectorId() + ": "
+                                    + e.getMessage());
                 }
             }
-            outWriter.append(hierarchyInfo.toString());
-            outWriter.print(", "); // close hierarchy
+        }
+        outWriter.print("\"state\":");
+        outWriter.append(sharedStates.toString());
+        outWriter.print(", "); // close states
+
+        // TODO This should be optimized. The type only needs to be
+        // sent once for each connector id + on refresh
 
+        JSONObject connectorTypes = new JSONObject();
+        for (Connector connector : dirtyVisibleConnectors) {
+            String connectorType = paintTarget.getTag((Paintable) connector);
+            try {
+                connectorTypes.put(connector.getConnectorId(), connectorType);
+            } catch (JSONException e) {
+                throw new PaintException(
+                        "Failed to send connector type for connector "
+                                + connector.getConnectorId() + ": "
+                                + e.getMessage());
+            }
         }
+        outWriter.print("\"types\":");
+        outWriter.append(connectorTypes.toString());
+        outWriter.print(", "); // close states
 
-        // send server to client RPC calls for components in the root, in call
-        // order
-        if (!rpcPendingQueue.isEmpty()) {
-            // collect RPC calls from components in the root in the order in
-            // which they were performed, remove the calls from components
-            List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(rpcPendingQueue);
-
-            JSONArray rpcCalls = new JSONArray();
-            for (ClientMethodInvocation invocation : pendingInvocations) {
-                // add invocation to rpcCalls
-                try {
-                    JSONArray invocationJson = new JSONArray();
-                    invocationJson
-                            .put(getPaintableId(invocation.getPaintable()));
-                    invocationJson.put(invocation.getInterfaceName());
-                    invocationJson.put(invocation.getMethodName());
-                    JSONArray paramJson = new JSONArray();
-                    for (int i = 0; i < invocation.getParameters().length; ++i) {
-                        paramJson.put(JsonCodec.encode(
-                                invocation.getParameters()[i], this));
+        // Send update hierarchy information to the client.
+
+        // This could be optimized aswell to send only info if hierarchy has
+        // actually changed. Much like with the shared state. Note though
+        // that an empty hierarchy is information aswell (e.g. change from 1
+        // child to 0 children)
+
+        outWriter.print("\"hierarchy\":");
+
+        JSONObject hierarchyInfo = new JSONObject();
+        for (Connector connector : dirtyVisibleConnectors) {
+            if (connector instanceof HasComponents) {
+                HasComponents parent = (HasComponents) connector;
+                String parentConnectorId = parent.getConnectorId();
+                JSONArray children = new JSONArray();
+
+                for (Component child : getChildComponents(parent)) {
+                    if (isVisible(child)) {
+                        children.put(child.getConnectorId());
                     }
-                    invocationJson.put(paramJson);
-                    rpcCalls.put(invocationJson);
+                }
+                try {
+                    hierarchyInfo.put(parentConnectorId, children);
                 } catch (JSONException e) {
                     throw new PaintException(
-                            "Failed to serialize RPC method call parameters for paintable "
-                                    + getPaintableId(invocation.getPaintable())
-                                    + " method "
-                                    + invocation.getInterfaceName() + "."
-                                    + invocation.getMethodName() + ": "
+                            "Failed to send hierarchy information about "
+                                    + parentConnectorId + " to the client: "
                                     + e.getMessage());
                 }
-
             }
+        }
+        outWriter.append(hierarchyInfo.toString());
+        outWriter.print(", "); // close hierarchy
+
+        // send server to client RPC calls for components in the root, in call
+        // order
+
+        // collect RPC calls from components in the root in the order in
+        // which they were performed, remove the calls from components
+
+        LinkedList<ClientConnector> rpcPendingQueue = new LinkedList<ClientConnector>(
+                (Collection<? extends ClientConnector>) dirtyVisibleConnectors);
+        List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(rpcPendingQueue);
 
-            if (rpcCalls.length() > 0) {
-                outWriter.print("\"rpc\" : ");
-                outWriter.append(rpcCalls.toString());
-                outWriter.print(", "); // close rpc
+        JSONArray rpcCalls = new JSONArray();
+        for (ClientMethodInvocation invocation : pendingInvocations) {
+            // add invocation to rpcCalls
+            try {
+                JSONArray invocationJson = new JSONArray();
+                invocationJson.put(invocation.getConnector().getConnectorId());
+                invocationJson.put(invocation.getInterfaceName());
+                invocationJson.put(invocation.getMethodName());
+                JSONArray paramJson = new JSONArray();
+                for (int i = 0; i < invocation.getParameters().length; ++i) {
+                    paramJson.put(JsonCodec.encode(
+                            invocation.getParameters()[i], application));
+                }
+                invocationJson.put(paramJson);
+                rpcCalls.put(invocationJson);
+            } catch (JSONException e) {
+                throw new PaintException(
+                        "Failed to serialize RPC method call parameters for connector "
+                                + invocation.getConnector().getConnectorId()
+                                + " method " + invocation.getInterfaceName()
+                                + "." + invocation.getMethodName() + ": "
+                                + e.getMessage());
             }
+
+        }
+
+        if (rpcCalls.length() > 0) {
+            outWriter.print("\"rpc\" : ");
+            outWriter.append(rpcCalls.toString());
+            outWriter.print(", "); // close rpc
         }
 
         outWriter.print("\"meta\" : {");
@@ -1027,11 +983,11 @@ public abstract class AbstractCommunicationManager implements
                 }
                 outWriter.write("]");
             }
-            if (highLightedPaintable != null) {
+            if (highlightedConnector != null) {
                 outWriter.write(", \"hl\":\"");
-                outWriter.write(paintableIdMap.get(highLightedPaintable));
+                outWriter.write(highlightedConnector.getConnectorId());
                 outWriter.write("\"");
-                highLightedPaintable = null;
+                highlightedConnector = null;
             }
         }
 
@@ -1123,6 +1079,8 @@ public abstract class AbstractCommunicationManager implements
         Collection<Class<? extends Paintable>> usedPaintableTypes = paintTarget
                 .getUsedPaintableTypes();
         boolean typeMappingsOpen = false;
+        ClientCache clientCache = getClientCache(root);
+
         for (Class<? extends Paintable> class1 : usedPaintableTypes) {
             if (clientCache.cache(class1)) {
                 // client does not know the mapping key for this type, send
@@ -1208,10 +1166,10 @@ public abstract class AbstractCommunicationManager implements
      * @return ordered list of pending RPC calls
      */
     private List<ClientMethodInvocation> collectPendingRpcCalls(
-            LinkedList<Paintable> rpcPendingQueue) {
+            LinkedList<ClientConnector> rpcPendingQueue) {
         List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
-        for (Paintable paintable : rpcPendingQueue) {
-            List<ClientMethodInvocation> paintablePendingRpc = paintable
+        for (ClientConnector connector : rpcPendingQueue) {
+            List<ClientMethodInvocation> paintablePendingRpc = connector
                     .retrievePendingRpcCalls();
             if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) {
                 List<ClientMethodInvocation> oldPendingRpc = pendingInvocations;
@@ -1262,30 +1220,6 @@ public abstract class AbstractCommunicationManager implements
         return requestThemeName;
     }
 
-    public void makeAllPaintablesDirty(Root root) {
-        // If repaint is requested, clean all ids in this root window
-        for (final Iterator<String> it = idPaintableMap.keySet().iterator(); it
-                .hasNext();) {
-            final Component c = (Component) idPaintableMap.get(it.next());
-            if (isChildOf(root, c)) {
-                it.remove();
-                paintableIdMap.remove(c);
-            }
-        }
-        // clean WindowCache
-        getClientCache(root).clear();
-    }
-
-    /**
-     * Called when communication manager stops listening for repaints for given
-     * component.
-     * 
-     * @param p
-     */
-    protected void unregisterPaintable(Component p) {
-        p.removeListener(this);
-    }
-
     /**
      * Returns false if the cross site request forgery protection is turned off.
      * 
@@ -1458,20 +1392,14 @@ public abstract class AbstractCommunicationManager implements
                 if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE
                         .equals(interfaceName)) {
                     // handle other RPC calls than variable changes
-                    applyInvocation(invocation);
+                    applyInvocation(app, invocation);
                     continue;
                 }
 
-                final VariableOwner owner = getVariableOwner(invocation
-                        .getConnectorId());
+                final VariableOwner owner = (VariableOwner) getConnector(app,
+                        invocation.getConnectorId());
 
-                boolean connectorEnabled;
-                if (owner instanceof Connector) {
-                    connectorEnabled = ((Connector) owner).isConnectorEnabled();
-                } else {
-                    // TODO Remove
-                    connectorEnabled = owner.isEnabled();
-                }
+                boolean connectorEnabled = owner.isEnabled();
 
                 if (owner != null && connectorEnabled) {
                     VariableChange change = new VariableChange(invocation);
@@ -1558,15 +1486,17 @@ public abstract class AbstractCommunicationManager implements
      * Execute an RPC call from the client by finding its target and letting the
      * RPC mechanism call the correct method for it.
      * 
+     * @param app
+     * 
      * @param invocation
      */
-    protected void applyInvocation(MethodInvocation invocation) {
-        final RpcTarget target = getRpcTarget(invocation.getConnectorId());
-        if (null != target) {
-            ServerRpcManager.applyInvocation(target, invocation);
+    protected void applyInvocation(Application app, MethodInvocation invocation) {
+        Connector c = app.getConnector(invocation.getConnectorId());
+        if (c instanceof RpcTarget) {
+            ServerRpcManager.applyInvocation((RpcTarget) c, invocation);
         } else {
             // TODO better exception?
-            throw new RuntimeException("No RPC target for paintable "
+            throw new RuntimeException("No RPC target for connector "
                     + invocation.getConnectorId());
         }
     }
@@ -1596,7 +1526,7 @@ public abstract class AbstractCommunicationManager implements
             Object[] parameters = new Object[parametersJson.length()];
             for (int j = 0; j < parametersJson.length(); ++j) {
                 parameters[j] = JsonCodec.decode(
-                        parametersJson.getJSONArray(j), this);
+                        parametersJson.getJSONArray(j), application);
             }
             MethodInvocation invocation = new MethodInvocation(connectorId,
                     interfaceName, methodName, parameters);
@@ -1610,34 +1540,17 @@ public abstract class AbstractCommunicationManager implements
         owner.changeVariables(source, m);
     }
 
-    protected VariableOwner getVariableOwner(String string) {
-        VariableOwner owner = (VariableOwner) idPaintableMap.get(string);
-        if (owner == null && string.startsWith("DD")) {
+    protected Connector getConnector(Application app, String connectorId) {
+        Connector c = app.getConnector(connectorId);
+        if (c == null
+                && connectorId.equals(getDragAndDropService().getConnectorId())) {
             return getDragAndDropService();
         }
-        return owner;
-    }
 
-    /**
-     * Returns the RPC call target for a paintable ID.
-     * 
-     * @since 7.0
-     * 
-     * @param string
-     *            paintable ID
-     * @return RPC call target or null if none found
-     */
-    protected RpcTarget getRpcTarget(String string) {
-        // TODO improve this - VariableOwner and RpcManager separate?
-        VariableOwner owner = getVariableOwner(string);
-        if (owner instanceof RpcTarget) {
-            return (RpcTarget) owner;
-        } else {
-            return null;
-        }
+        return c;
     }
 
-    private VariableOwner getDragAndDropService() {
+    private DragAndDropService getDragAndDropService() {
         if (dragAndDropService == null) {
             dragAndDropService = new DragAndDropService(this);
         }
@@ -1973,56 +1886,6 @@ public abstract class AbstractCommunicationManager implements
         outWriter.print("for(;;);[{");
     }
 
-    public Paintable getPaintable(String paintableId) {
-        return idPaintableMap.get(paintableId);
-    }
-
-    /**
-     * Gets the Paintable Id. If Paintable has debug id set it will be used
-     * prefixed with "PID_S". Otherwise a sequenced ID is created.
-     * 
-     * @param paintable
-     * @return the paintable Id.
-     */
-    public String getPaintableId(Paintable paintable) {
-
-        String id = paintableIdMap.get(paintable);
-        if (id == null) {
-            // use testing identifier as id if set
-            id = paintable.getDebugId();
-            if (id == null) {
-                id = "PID" + Integer.toString(idSequence++);
-            } else {
-                id = "PID_S" + id;
-            }
-            Paintable old = idPaintableMap.put(id, paintable);
-            if (old != null && old != paintable) {
-                /*
-                 * Two paintables have the same id. We still make sure the old
-                 * one is a component which is still attached to the
-                 * application. This is just a precaution and should not be
-                 * absolutely necessary.
-                 */
-
-                if (old instanceof Component
-                        && ((Component) old).getApplication() != null) {
-                    throw new IllegalStateException("Two paintables ("
-                            + paintable.getClass().getSimpleName() + ","
-                            + old.getClass().getSimpleName()
-                            + ") have been assigned the same id: "
-                            + paintable.getDebugId());
-                }
-            }
-            paintableIdMap.put(paintable, id);
-        }
-
-        return id;
-    }
-
-    public boolean hasPaintableId(Paintable paintable) {
-        return paintableIdMap.containsKey(paintable);
-    }
-
     /**
      * Returns dirty components which are in given window. Components in an
      * invisible subtrees are omitted.
@@ -2031,65 +1894,26 @@ public abstract class AbstractCommunicationManager implements
      *            root window for which dirty components is to be fetched
      * @return
      */
-    private ArrayList<Paintable> getDirtyVisibleComponents(Root r) {
-        final ArrayList<Paintable> resultset = new ArrayList<Paintable>(
-                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
-        // once and any unmodified components will be painted as "cached=true".
-
-        for (final Iterator<Paintable> i = dirtyPaintables.iterator(); i
-                .hasNext();) {
-            final Paintable p = i.next();
-            if (p instanceof Component) {
-                final Component component = (Component) p;
-                if (component.getApplication() == null) {
-                    // component is detached after requestRepaint is called
-                    resultset.remove(p);
-                    i.remove();
-                } else {
-                    Root componentsRoot = component.getRoot();
-                    if (componentsRoot == null) {
-                        // This should not happen unless somebody has overriden
-                        // getApplication or getWindow in an illegal way.
-                        throw new IllegalStateException(
-                                "component.getWindow() returned null for a component attached to the application");
-                    }
-                    // if (componentsRoot.getParent() != null) {
-                    // // this is a subwindow
-                    // componentsRoot = componentsRoot.getParent();
-                    // }
-                    if (componentsRoot != r) {
-                        resultset.remove(p);
-                    } else if (component.getParent() != null
-                            && !isVisible(component.getParent())) {
-                        /*
-                         * Do not return components in an invisible subtree.
-                         * 
-                         * Components that are invisible in visible subree, must
-                         * be rendered (to let client know that they need to be
-                         * hidden).
-                         */
-                        resultset.remove(p);
-                    }
-                }
+    private ArrayList<Component> getDirtyVisibleComponents(
+            DirtyConnectorTracker dirtyConnectorTracker) {
+        ArrayList<Component> dirtyComponents = new ArrayList<Component>();
+        for (Component c : dirtyConnectorTracker.getDirtyComponents()) {
+            if (isVisible(c)) {
+                dirtyComponents.add(c);
             }
         }
 
-        return resultset;
+        return dirtyComponents;
     }
 
     /**
      * @see com.vaadin.terminal.Paintable.RepaintRequestListener#repaintRequested(com.vaadin.terminal.Paintable.RepaintRequestEvent)
      */
     public void repaintRequested(RepaintRequestEvent event) {
-        final Paintable p = event.getPaintable();
-        if (!dirtyPaintables.contains(p)) {
-            dirtyPaintables.add(p);
-        }
+        // final Paintable p = event.getPaintable();
+        // if (!dirtyPaintables.contains(p)) {
+        // dirtyPaintables.add(p);
+        // }
     }
 
     /**
@@ -2099,8 +1923,8 @@ public abstract class AbstractCommunicationManager implements
      * @param paintable
      */
     private void paintablePainted(Paintable paintable) {
-        dirtyPaintables.remove(paintable);
-        paintable.requestRepaintRequests();
+        // dirtyPaintables.remove(paintable);
+        // paintable.requestRepaintRequests();
     }
 
     /**
@@ -2145,23 +1969,6 @@ public abstract class AbstractCommunicationManager implements
         }
     }
 
-    /**
-     * Helper method to test if a component contains another
-     * 
-     * @param parent
-     * @param child
-     */
-    private static boolean isChildOf(Component parent, Component child) {
-        Component p = child.getParent();
-        while (p != null) {
-            if (parent == p) {
-                return true;
-            }
-            p = p.getParent();
-        }
-        return false;
-    }
-
     protected class InvalidUIDLSecurityKeyException extends
             GeneralSecurityException {
 
@@ -2210,10 +2017,10 @@ public abstract class AbstractCommunicationManager implements
 
     }
 
-    abstract String getStreamVariableTargetUrl(VariableOwner owner,
-            String name, StreamVariable value);
+    abstract String getStreamVariableTargetUrl(Connector owner, String name,
+            StreamVariable value);
 
-    abstract protected void cleanStreamVariable(VariableOwner owner, String name);
+    abstract protected void cleanStreamVariable(Connector owner, String name);
 
     /**
      * Gets the bootstrap handler that should be used for generating the pages
@@ -2297,7 +2104,6 @@ public abstract class AbstractCommunicationManager implements
     protected String getInitialUIDL(WrappedRequest request, Root root)
             throws PaintException {
         // TODO maybe unify writeUidlResponse()?
-        makeAllPaintablesDirty(root);
         StringWriter sWriter = new StringWriter();
         PrintWriter pWriter = new PrintWriter(sWriter);
         pWriter.print("{");
@@ -2461,4 +2267,13 @@ public abstract class AbstractCommunicationManager implements
             return b;
         }
     }
+
+    @Deprecated
+    public String getPaintableId(Paintable paintable) {
+        if (paintable instanceof Connector) {
+            return ((Connector) paintable).getConnectorId();
+        }
+        throw new RuntimeException("Paintable " + paintable
+                + " must implement Connector");
+    }
 }
diff --git a/src/com/vaadin/terminal/gwt/server/ClientConnector.java b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
new file mode 100644 (file)
index 0000000..e2943b4
--- /dev/null
@@ -0,0 +1,19 @@
+package com.vaadin.terminal.gwt.server;
+
+import java.util.List;
+
+import com.vaadin.terminal.gwt.client.Connector;
+
+public interface ClientConnector extends Connector {
+    /**
+     * Returns the list of pending server to client RPC calls and clears the
+     * list.
+     * 
+     * @return unmodifiable ordered list of pending server to client method
+     *         calls (not null)
+     * 
+     * @since 7.0
+     */
+    public List<ClientMethodInvocation> retrievePendingRpcCalls();
+
+}
index 8133a617ca9c26ee2f750950e8117195e3f65bba..2edcb8a9f2cb91eef79b80d72c59c71060dcf26f 100644 (file)
@@ -6,17 +6,15 @@ package com.vaadin.terminal.gwt.server;
 
 import java.io.Serializable;
 
-import com.vaadin.terminal.Paintable;
-
 /**
  * Internal class for keeping track of pending server to client method
- * invocations for a Paintable.
+ * invocations for a Connector.
  * 
  * @since 7.0
  */
 public class ClientMethodInvocation implements Serializable,
         Comparable<ClientMethodInvocation> {
-    private final Paintable paintable;
+    private final ClientConnector connector;
     private final String interfaceName;
     private final String methodName;
     private final Object[] parameters;
@@ -26,17 +24,17 @@ public class ClientMethodInvocation implements Serializable,
     // TODO may cause problems when clustering etc.
     private static long counter = 0;
 
-    public ClientMethodInvocation(Paintable paintable, String interfaceName,
-            String methodName, Object[] parameters) {
-        this.paintable = paintable;
+    public ClientMethodInvocation(ClientConnector connector,
+            String interfaceName, String methodName, Object[] parameters) {
+        this.connector = connector;
         this.interfaceName = interfaceName;
         this.methodName = methodName;
         this.parameters = (null != parameters) ? parameters : new Object[0];
         sequenceNumber = ++counter;
     }
 
-    public Paintable getPaintable() {
-        return paintable;
+    public ClientConnector getConnector() {
+        return connector;
     }
 
     public String getInterfaceName() {
index 3a9b8ff2db23411b3ddd7218a13d9bd80e8a7f26..3dd2eb97fd74348e13ab146fa72243d17a30f171 100644 (file)
@@ -8,6 +8,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
 
@@ -15,12 +16,10 @@ import javax.servlet.ServletContext;
 
 import com.vaadin.Application;
 import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.Paintable;
 import com.vaadin.terminal.StreamVariable;
-import com.vaadin.terminal.VariableOwner;
 import com.vaadin.terminal.WrappedRequest;
 import com.vaadin.terminal.WrappedResponse;
-import com.vaadin.ui.Component;
+import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.ui.Root;
 
 /**
@@ -62,6 +61,8 @@ public class CommunicationManager extends AbstractCommunicationManager {
     /**
      * Handles file upload request submitted via Upload component.
      * 
+     * @param application
+     * 
      * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
      * 
      * @param request
@@ -69,9 +70,9 @@ public class CommunicationManager extends AbstractCommunicationManager {
      * @throws IOException
      * @throws InvalidUIDLSecurityKeyException
      */
-    public void handleFileUpload(WrappedRequest request,
-            WrappedResponse response) throws IOException,
-            InvalidUIDLSecurityKeyException {
+    public void handleFileUpload(Application application,
+            WrappedRequest request, WrappedResponse response)
+            throws IOException, InvalidUIDLSecurityKeyException {
 
         /*
          * URI pattern: APP/UPLOAD/[PID]/[NAME]/[SECKEY] See #createReceiverUrl
@@ -92,7 +93,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
         String secKey = streamVariableToSeckey.get(streamVariable);
         if (secKey.equals(parts[2])) {
 
-            VariableOwner source = getVariableOwner(paintableId);
+            Connector source = getConnector(application, paintableId);
             String contentType = request.getContentType();
             if (contentType.contains("boundary")) {
                 // Multipart requests contain boundary string
@@ -113,18 +114,26 @@ public class CommunicationManager extends AbstractCommunicationManager {
     }
 
     @Override
-    protected void unregisterPaintable(Component p) {
-        /* Cleanup possible receivers */
+    protected void postPaint(Root root) {
+        super.postPaint(root);
+
+        Application application = root.getApplication();
         if (pidToNameToStreamVariable != null) {
-            Map<String, StreamVariable> removed = pidToNameToStreamVariable
-                    .remove(getPaintableId(p));
-            if (removed != null) {
-                for (String key : removed.keySet()) {
-                    streamVariableToSeckey.remove(removed.get(key));
+            Iterator<String> iterator = pidToNameToStreamVariable.keySet()
+                    .iterator();
+            while (iterator.hasNext()) {
+                String connectorId = iterator.next();
+                if (application.getConnector(connectorId) == null) {
+                    // Owner is no longer attached to the application
+                    Map<String, StreamVariable> removed = pidToNameToStreamVariable
+                            .get(connectorId);
+                    for (String key : removed.keySet()) {
+                        streamVariableToSeckey.remove(removed.get(key));
+                    }
+                    iterator.remove();
                 }
             }
         }
-        super.unregisterPaintable(p);
 
     }
 
@@ -133,7 +142,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
     private Map<StreamVariable, String> streamVariableToSeckey;
 
     @Override
-    String getStreamVariableTargetUrl(VariableOwner owner, String name,
+    String getStreamVariableTargetUrl(Connector owner, String name,
             StreamVariable value) {
         /*
          * We will use the same APP/* URI space as ApplicationResources but
@@ -147,7 +156,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
          * NAME and PID from URI forms a key to fetch StreamVariable when
          * handling post
          */
-        String paintableId = getPaintableId((Paintable) owner);
+        String paintableId = owner.getConnectorId();
         String key = paintableId + "/" + name;
 
         if (pidToNameToStreamVariable == null) {
@@ -176,12 +185,12 @@ public class CommunicationManager extends AbstractCommunicationManager {
     }
 
     @Override
-    protected void cleanStreamVariable(VariableOwner owner, String name) {
+    protected void cleanStreamVariable(Connector owner, String name) {
         Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable
-                .get(getPaintableId((Paintable) owner));
+                .get(owner.getConnectorId());
         nameToStreamVar.remove("name");
         if (nameToStreamVar.isEmpty()) {
-            pidToNameToStreamVariable.remove(getPaintableId((Paintable) owner));
+            pidToNameToStreamVariable.remove(owner.getConnectorId());
         }
     }
 
index 9fc8aa6bf06de1fd4096729e7216c4dd9a0eb959..335067ca7a546385a8895a613e99171f6286a816 100644 (file)
@@ -185,7 +185,7 @@ public class ComponentSizeValidator implements Serializable {
             clientJSON.write("{");
 
             Component parent = component.getParent();
-            String paintableId = communicationManager.getPaintableId(component);
+            String paintableId = component.getConnectorId();
 
             clientJSON.print("id:\"" + paintableId + "\"");
 
index 3a923c18408998a68f47e561f0f6fe193b4e233d..a637db28403395fb4c9495f92e9990f2e5c0315e 100644 (file)
@@ -18,10 +18,13 @@ import com.vaadin.event.dd.TargetDetailsImpl;
 import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
 import com.vaadin.terminal.PaintException;
 import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
 import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager.DragEventType;
 import com.vaadin.ui.Component;
 
-public class DragAndDropService implements VariableOwner {
+public class DragAndDropService implements VariableOwner, Connector {
 
     private static final Logger logger = Logger
             .getLogger(DragAndDropService.class.getName());
@@ -177,7 +180,7 @@ public class DragAndDropService implements VariableOwner {
     }
 
     public boolean isEnabled() {
-        return true;
+        return isConnectorEnabled();
     }
 
     public boolean isImmediate() {
@@ -212,4 +215,18 @@ public class DragAndDropService implements VariableOwner {
         }
         return false;
     }
+
+    public SharedState getState() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String getConnectorId() {
+        return VDragAndDropManager.DD_SERVICE;
+    }
+
+    public boolean isConnectorEnabled() {
+        // Drag'n'drop can't be disabled
+        return true;
+    }
 }
index 5d2e39f10f244ac3cb4f2c6b6597a995e906d928..9fea64a1c7e44759c43c7485a24e5db9cc93c053 100644 (file)
@@ -16,6 +16,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import com.vaadin.Application;
 import com.vaadin.external.json.JSONArray;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
@@ -35,8 +36,7 @@ public class JsonCodec implements Serializable {
 
     static {
         registerType(String.class, JsonEncoder.VTYPE_STRING);
-        registerType(Paintable.class, JsonEncoder.VTYPE_PAINTABLE);
-        registerType(Connector.class, JsonEncoder.VTYPE_PAINTABLE);
+        registerType(Connector.class, JsonEncoder.VTYPE_CONNECTOR);
         registerType(Boolean.class, JsonEncoder.VTYPE_BOOLEAN);
         registerType(Integer.class, JsonEncoder.VTYPE_INTEGER);
         registerType(Float.class, JsonEncoder.VTYPE_FLOAT);
@@ -60,27 +60,28 @@ public class JsonCodec implements Serializable {
      * 
      * @param value
      *            JSON array with two elements
-     * @param idMapper
+     * @param application
      *            mapper between paintable ID and {@link Paintable} objects
      * @return converted value (does not contain JSON types)
      * @throws JSONException
      *             if the conversion fails
      */
-    public static Object decode(JSONArray value, PaintableIdMapper idMapper)
+    public static Object decode(JSONArray value, Application application)
             throws JSONException {
-        return decodeVariableValue(value.getString(0), value.get(1), idMapper);
+        return decodeVariableValue(value.getString(0), value.get(1),
+                application);
     }
 
     private static Object decodeVariableValue(String variableType,
-            Object value, PaintableIdMapper idMapper) throws JSONException {
+            Object value, Application application) throws JSONException {
         Object val = null;
         // TODO type checks etc.
         if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) {
-            val = decodeArray((JSONArray) value, idMapper);
+            val = decodeArray((JSONArray) value, application);
         } else if (JsonEncoder.VTYPE_LIST.equals(variableType)) {
-            val = decodeList((JSONArray) value, idMapper);
+            val = decodeList((JSONArray) value, application);
         } else if (JsonEncoder.VTYPE_MAP.equals(variableType)) {
-            val = decodeMap((JSONObject) value, idMapper);
+            val = decodeMap((JSONObject) value, application);
         } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) {
             val = decodeStringArray((JSONArray) value);
         } else if (JsonEncoder.VTYPE_STRING.equals(variableType)) {
@@ -100,27 +101,26 @@ public class JsonCodec implements Serializable {
         } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) {
             // TODO handle properly
             val = Boolean.valueOf(String.valueOf(value));
-        } else if (JsonEncoder.VTYPE_PAINTABLE.equals(variableType)) {
-            // TODO handle properly
-            val = idMapper.getPaintable(String.valueOf(value));
+        } else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) {
+            val = application.getConnector(String.valueOf(value));
         } else if (JsonEncoder.VTYPE_NULL.equals(variableType)) {
             val = null;
         } else {
             // Try to decode object using fields
-            return decodeObject(variableType, (JSONObject) value, idMapper);
+            return decodeObject(variableType, (JSONObject) value, application);
 
         }
 
         return val;
     }
 
-    private static Object decodeMap(JSONObject jsonMap,
-            PaintableIdMapper idMapper) throws JSONException {
+    private static Object decodeMap(JSONObject jsonMap, Application application)
+            throws JSONException {
         HashMap<String, Object> map = new HashMap<String, Object>();
         Iterator<String> it = jsonMap.keys();
         while (it.hasNext()) {
             String key = it.next();
-            map.put(key, decode(jsonMap.getJSONArray(key), idMapper));
+            map.put(key, decode(jsonMap.getJSONArray(key), application));
         }
         return map;
     }
@@ -136,18 +136,18 @@ public class JsonCodec implements Serializable {
     }
 
     private static Object decodeArray(JSONArray jsonArray,
-            PaintableIdMapper idMapper) throws JSONException {
-        List list = decodeList(jsonArray, idMapper);
+            Application application) throws JSONException {
+        List list = decodeList(jsonArray, application);
         return list.toArray(new Object[list.size()]);
     }
 
-    private static List decodeList(JSONArray jsonArray,
-            PaintableIdMapper idMapper) throws JSONException {
+    private static List decodeList(JSONArray jsonArray, Application application)
+            throws JSONException {
         List<Object> list = new ArrayList<Object>();
         for (int i = 0; i < jsonArray.length(); ++i) {
             // each entry always has two elements: type and value
             JSONArray entryArray = jsonArray.getJSONArray(i);
-            list.add(decode(entryArray, idMapper));
+            list.add(decode(entryArray, application));
         }
         return list;
     }
@@ -158,19 +158,19 @@ public class JsonCodec implements Serializable {
      * 
      * @param value
      *            value to convert
-     * @param idMapper
+     * @param application
      *            mapper between paintable ID and {@link Paintable} objects
      * @return JSON representation of the value
      * @throws JSONException
      *             if encoding a value fails (e.g. NaN or infinite number)
      */
-    public static JSONArray encode(Object value, PaintableIdMapper idMapper)
+    public static JSONArray encode(Object value, Application application)
             throws JSONException {
-        return encode(value, null, idMapper);
+        return encode(value, null, application);
     }
 
     public static JSONArray encode(Object value, Class<?> valueType,
-            PaintableIdMapper idMapper) throws JSONException {
+            Application application) throws JSONException {
 
         if (null == value) {
             return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL);
@@ -189,20 +189,20 @@ public class JsonCodec implements Serializable {
             return combineTypeAndValue(getTransportType(value), value);
         } else if (value instanceof List) {
             List list = (List) value;
-            JSONArray jsonArray = encodeList(list, idMapper);
+            JSONArray jsonArray = encodeList(list, application);
             return combineTypeAndValue(JsonEncoder.VTYPE_LIST, jsonArray);
         } else if (value instanceof Object[]) {
             Object[] array = (Object[]) value;
-            JSONArray jsonArray = encodeArrayContents(array, idMapper);
+            JSONArray jsonArray = encodeArrayContents(array, application);
             return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray);
         } else if (value instanceof Map) {
             Map<String, Object> map = (Map<String, Object>) value;
-            JSONObject jsonMap = encodeMapContents(map, idMapper);
+            JSONObject jsonMap = encodeMapContents(map, application);
             return combineTypeAndValue(JsonEncoder.VTYPE_MAP, jsonMap);
-        } else if (value instanceof Paintable) {
-            Paintable paintable = (Paintable) value;
-            return combineTypeAndValue(JsonEncoder.VTYPE_PAINTABLE,
-                    idMapper.getPaintableId(paintable));
+        } else if (value instanceof Connector) {
+            Connector connector = (Connector) value;
+            return combineTypeAndValue(JsonEncoder.VTYPE_CONNECTOR,
+                    connector.getConnectorId());
         } else if (getTransportType(value) != null) {
             return combineTypeAndValue(getTransportType(value),
                     String.valueOf(value));
@@ -214,11 +214,11 @@ public class JsonCodec implements Serializable {
             }
 
             return combineTypeAndValue(valueType.getCanonicalName(),
-                    encodeObject(value, idMapper));
+                    encodeObject(value, application));
         }
     }
 
-    private static Object encodeObject(Object value, PaintableIdMapper idMapper)
+    private static Object encodeObject(Object value, Application application)
             throws JSONException {
         JSONObject jsonMap = new JSONObject();
 
@@ -232,7 +232,8 @@ public class JsonCodec implements Serializable {
                 }
                 Method getterMethod = pd.getReadMethod();
                 Object fieldValue = getterMethod.invoke(value, (Object[]) null);
-                jsonMap.put(fieldName, encode(fieldValue, fieldType, idMapper));
+                jsonMap.put(fieldName,
+                        encode(fieldValue, fieldType, application));
             }
         } catch (Exception e) {
             // TODO: Should exceptions be handled in a different way?
@@ -242,7 +243,7 @@ public class JsonCodec implements Serializable {
     }
 
     private static Object decodeObject(String type,
-            JSONObject serializedObject, PaintableIdMapper idMapper)
+            JSONObject serializedObject, Application application)
             throws JSONException {
 
         Class<?> cls;
@@ -260,7 +261,7 @@ public class JsonCodec implements Serializable {
                 JSONArray encodedObject = serializedObject
                         .getJSONArray(fieldName);
                 pd.getWriteMethod().invoke(decodedObject,
-                        decode(encodedObject, idMapper));
+                        decode(encodedObject, application));
             }
 
             return decodedObject;
@@ -280,32 +281,32 @@ public class JsonCodec implements Serializable {
     }
 
     private static JSONArray encodeArrayContents(Object[] array,
-            PaintableIdMapper idMapper) throws JSONException {
+            Application application) throws JSONException {
         JSONArray jsonArray = new JSONArray();
         for (Object o : array) {
             // TODO handle object graph loops?
-            jsonArray.put(encode(o, idMapper));
+            jsonArray.put(encode(o, application));
         }
         return jsonArray;
     }
 
-    private static JSONArray encodeList(List list, PaintableIdMapper idMapper)
+    private static JSONArray encodeList(List list, Application application)
             throws JSONException {
         JSONArray jsonArray = new JSONArray();
         for (Object o : list) {
             // TODO handle object graph loops?
-            jsonArray.put(encode(o, idMapper));
+            jsonArray.put(encode(o, application));
         }
         return jsonArray;
     }
 
     private static JSONObject encodeMapContents(Map<String, Object> map,
-            PaintableIdMapper idMapper) throws JSONException {
+            Application application) throws JSONException {
         JSONObject jsonMap = new JSONObject();
         for (String mapKey : map.keySet()) {
             // TODO handle object graph loops?
             Object mapValue = map.get(mapKey);
-            jsonMap.put(mapKey, encode(mapValue, idMapper));
+            jsonMap.put(mapKey, encode(mapValue, application));
         }
         return jsonMap;
     }
index 8a57d2110ea7b73251104c5f40ba56924fa5f584..930a5bec34b463d4b6bc8b20f3a3dfc3af9ca44f 100644 (file)
@@ -34,9 +34,9 @@ import com.vaadin.terminal.Resource;
 import com.vaadin.terminal.StreamVariable;
 import com.vaadin.terminal.ThemeResource;
 import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.ui.Alignment;
 import com.vaadin.ui.ClientWidget;
-import com.vaadin.ui.Component;
 import com.vaadin.ui.CustomLayout;
 import com.vaadin.ui.Root;
 
@@ -89,7 +89,7 @@ public class JsonPaintTarget implements PaintTarget {
 
     private final Collection<Paintable> paintedComponents = new HashSet<Paintable>();
 
-    private Collection<Paintable> identifiersCreatedDueRefPaint;
+    // private Collection<Paintable> identifiersCreatedDueRefPaint;
 
     private Collection<Paintable> deferredPaintables;
 
@@ -687,49 +687,46 @@ public class JsonPaintTarget implements PaintTarget {
      */
     public PaintStatus startPaintable(Paintable paintable, String tagName)
             throws PaintException {
+        boolean topLevelPaintable = openPaintables.isEmpty();
+
+        System.out.println("startPaintable for "
+                + paintable.getClass().getName() + "@"
+                + Integer.toHexString(paintable.hashCode()));
         startTag(tagName, true);
-        final boolean isPreviouslyPainted = manager.hasPaintableId(paintable)
-                && (identifiersCreatedDueRefPaint == null || !identifiersCreatedDueRefPaint
-                        .contains(paintable))
-                && !deferredPaintables.contains(paintable);
+
+        openPaintables.push(paintable);
+        openPaintableTags.push(tagName);
+
         final String id = manager.getPaintableId(paintable);
         paintable.addListener(manager);
         addAttribute("id", id);
 
         // queue for painting later if already painting a paintable
-        boolean topLevelPaintableTag = openPaintables.isEmpty();
-
-        openPaintables.push(paintable);
-        openPaintableTags.push(tagName);
-
-        if (!topLevelPaintableTag) {
+        if (!topLevelPaintable) {
+            // if (!deferredPaintables.contains(paintable)) {
             // notify manager: add to paint queue instead of painting now
-            manager.queuePaintable(paintable);
-            deferredPaintables.add(paintable);
+            // manager.queuePaintable(paintable);
+            // deferredPaintables.add(paintable);
+            // }
             return PaintStatus.DEFER;
-        } else if (cacheEnabled && isPreviouslyPainted) {
-            // cached (unmodified) paintable, paint the it now
-            paintedComponents.add(paintable);
-            deferredPaintables.remove(paintable);
-            return PaintStatus.CACHED;
-        } else {
-            // not a nested paintable, paint the it now
-            paintedComponents.add(paintable);
-            deferredPaintables.remove(paintable);
+        }
 
-            if (paintable instanceof CustomLayout) {
-                customLayoutArgumentsOpen = true;
-            }
-            return PaintStatus.PAINTING;
+        // not a nested paintable, paint the it now
+        paintedComponents.add(paintable);
+        // deferredPaintables.remove(paintable);
+
+        if (paintable instanceof CustomLayout) {
+            customLayoutArgumentsOpen = true;
         }
+        return PaintStatus.PAINTING;
     }
 
     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) + "'.");
+                    + manager.getPaintableId(paintable) + "' expected: '"
+                    + manager.getPaintableId(openPaintable) + "'.");
         }
         // remove paintable from the stack
         openPaintables.pop();
@@ -738,12 +735,12 @@ public class JsonPaintTarget implements PaintTarget {
     }
 
     public String getPaintIdentifier(Paintable paintable) throws PaintException {
-        if (!manager.hasPaintableId(paintable)) {
-            if (identifiersCreatedDueRefPaint == null) {
-                identifiersCreatedDueRefPaint = new HashSet<Paintable>();
-            }
-            identifiersCreatedDueRefPaint.add(paintable);
-        }
+        // if (!manager.hasPaintableId(paintable)) {
+        // if (identifiersCreatedDueRefPaint == null) {
+        // identifiersCreatedDueRefPaint = new HashSet<Paintable>();
+        // }
+        // identifiersCreatedDueRefPaint.add(paintable);
+        // }
         return manager.getPaintableId(paintable);
     }
 
@@ -1021,22 +1018,22 @@ public class JsonPaintTarget implements PaintTarget {
         return usedResources;
     }
 
-    /**
-     * Method to check if paintable is already painted into this target.
-     * 
-     * @param p
-     * @return true if is not yet painted into this target and is connected to
-     *         app
-     */
-    public boolean needsToBePainted(Paintable p) {
-        if (paintedComponents.contains(p)) {
-            return false;
-        } else if (((Component) p).getApplication() == null) {
-            return false;
-        } else {
-            return true;
-        }
-    }
+    // /**
+    // * Method to check if paintable is already painted into this target.
+    // *
+    // * @param p
+    // * @return true if is not yet painted into this target and is connected to
+    // * app
+    // */
+    // public boolean needsToBePainted(Paintable p) {
+    // if (paintedComponents.contains(p)) {
+    // return false;
+    // } else if (((Component) p).getApplication() == null) {
+    // return false;
+    // } else {
+    // return true;
+    // }
+    // }
 
     private static final Map<Class<? extends Paintable>, Class<? extends Paintable>> widgetMappingCache = new HashMap<Class<? extends Paintable>, Class<? extends Paintable>>();
 
@@ -1195,7 +1192,8 @@ public class JsonPaintTarget implements PaintTarget {
 
     public void addVariable(VariableOwner owner, String name,
             StreamVariable value) throws PaintException {
-        String url = manager.getStreamVariableTargetUrl(owner, name, value);
+        String url = manager.getStreamVariableTargetUrl((Connector) owner,
+                name, value);
         if (url != null) {
             addVariable(owner, name, url);
         } // else { //NOP this was just a cleanup by component }
index 49bcb7a79cf2930ee16180cf9ef702c3fec4f76a..b3ec33a9e0f633ed2cfebeca8b7d450e8a125c0d 100644 (file)
@@ -6,6 +6,7 @@ package com.vaadin.terminal.gwt.server;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 import javax.portlet.MimeResponse;
@@ -22,12 +23,10 @@ import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
 import com.vaadin.terminal.DeploymentConfiguration;
 import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.Paintable;
 import com.vaadin.terminal.StreamVariable;
-import com.vaadin.terminal.VariableOwner;
 import com.vaadin.terminal.WrappedRequest;
 import com.vaadin.terminal.WrappedResponse;
-import com.vaadin.ui.Component;
+import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.ui.Root;
 
 /**
@@ -50,26 +49,36 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
         String contentType = request.getContentType();
         String name = request.getParameter("name");
         String ownerId = request.getParameter("rec-owner");
-        VariableOwner variableOwner = getVariableOwner(ownerId);
-        StreamVariable streamVariable = ownerToNameToStreamVariable.get(
-                variableOwner).get(name);
+        Connector owner = getConnector(getApplication(), ownerId);
+        StreamVariable streamVariable = ownerToNameToStreamVariable.get(owner)
+                .get(name);
 
         if (contentType.contains("boundary")) {
             doHandleSimpleMultipartFileUpload(request, response,
-                    streamVariable, name, variableOwner,
+                    streamVariable, name, owner,
                     contentType.split("boundary=")[1]);
         } else {
-            doHandleXhrFilePost(request, response, streamVariable, name,
-                    variableOwner, request.getContentLength());
+            doHandleXhrFilePost(request, response, streamVariable, name, owner,
+                    request.getContentLength());
         }
 
     }
 
     @Override
-    protected void unregisterPaintable(Component p) {
-        super.unregisterPaintable(p);
+    protected void postPaint(Root root) {
+        super.postPaint(root);
+
+        Application application = root.getApplication();
         if (ownerToNameToStreamVariable != null) {
-            ownerToNameToStreamVariable.remove(p);
+            Iterator<Connector> iterator = ownerToNameToStreamVariable.keySet()
+                    .iterator();
+            while (iterator.hasNext()) {
+                Connector owner = iterator.next();
+                if (application.getConnector(owner.getConnectorId()) == null) {
+                    // Owner is no longer attached to the application
+                    iterator.remove();
+                }
+            }
         }
     }
 
@@ -83,13 +92,13 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
         currentUidlResponse = null;
     }
 
-    private Map<VariableOwner, Map<String, StreamVariable>> ownerToNameToStreamVariable;
+    private Map<Connector, Map<String, StreamVariable>> ownerToNameToStreamVariable;
 
     @Override
-    String getStreamVariableTargetUrl(VariableOwner owner, String name,
+    String getStreamVariableTargetUrl(Connector owner, String name,
             StreamVariable value) {
         if (ownerToNameToStreamVariable == null) {
-            ownerToNameToStreamVariable = new HashMap<VariableOwner, Map<String, StreamVariable>>();
+            ownerToNameToStreamVariable = new HashMap<Connector, Map<String, StreamVariable>>();
         }
         Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable
                 .get(owner);
@@ -101,14 +110,14 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
         ResourceURL resurl = currentUidlResponse.createResourceURL();
         resurl.setResourceID("UPLOAD");
         resurl.setParameter("name", name);
-        resurl.setParameter("rec-owner", getPaintableId((Paintable) owner));
+        resurl.setParameter("rec-owner", owner.getConnectorId());
         resurl.setProperty("name", name);
-        resurl.setProperty("rec-owner", getPaintableId((Paintable) owner));
+        resurl.setProperty("rec-owner", owner.getConnectorId());
         return resurl.toString();
     }
 
     @Override
-    protected void cleanStreamVariable(VariableOwner owner, String name) {
+    protected void cleanStreamVariable(Connector owner, String name) {
         Map<String, StreamVariable> map = ownerToNameToStreamVariable
                 .get(owner);
         map.remove(name);
index e2e8a9693e6fc9a53e407c33861dd569fef54200..c4bfa9298ea08f6b0246900e7834209bc2429a33 100644 (file)
@@ -147,6 +147,8 @@ public abstract class AbstractComponent implements Component, MethodEventSource
      */
     private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
 
+    private String connectorId;
+
     /* Constructor */
 
     /**
@@ -651,6 +653,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
      * interface.
      */
     public void attach() {
+        getRoot().componentAttached(this);
         requestRepaint();
         if (!getState().isVisible()) {
             /*
@@ -676,6 +679,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
             // compiler happy
             actionManager.setViewer((Root) null);
         }
+        getRoot().componentDetached(this);
     }
 
     /**
@@ -1752,7 +1756,13 @@ public abstract class AbstractComponent implements Component, MethodEventSource
     }
 
     public String getConnectorId() {
-        throw new RuntimeException(
-                "TODO: Move connector id handling to AbstractComponent");
+        if (connectorId == null) {
+            if (getApplication() == null) {
+                throw new RuntimeException(
+                        "Component must be attached to an application when getConnectorId() is called for the first time");
+            }
+            connectorId = getApplication().createConnectorId(this);
+        }
+        return connectorId;
     }
 }
index 6ca4d5e2860cd02e01f7e5c8bdf484abe4abe150..ae6fcd937816a7fba07fac7002a7ace88fd34931 100644 (file)
@@ -18,7 +18,7 @@ import com.vaadin.terminal.Resource;
 import com.vaadin.terminal.Sizeable;
 import com.vaadin.terminal.VariableOwner;
 import com.vaadin.terminal.gwt.client.ComponentState;
-import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.server.ClientConnector;
 import com.vaadin.terminal.gwt.server.RpcTarget;
 
 /**
@@ -54,7 +54,7 @@ import com.vaadin.terminal.gwt.server.RpcTarget;
  * @VERSION@
  * @since 3.0
  */
-public interface Component extends Connector, Paintable, VariableOwner,
+public interface Component extends ClientConnector, Paintable, VariableOwner,
         Sizeable, Serializable, RpcTarget {
 
     /**
diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java
new file mode 100644 (file)
index 0000000..2e844e0
--- /dev/null
@@ -0,0 +1,102 @@
+package com.vaadin.ui;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import com.vaadin.terminal.Paintable.RepaintRequestEvent;
+import com.vaadin.terminal.Paintable.RepaintRequestListener;
+
+public class DirtyConnectorTracker implements RepaintRequestListener {
+    private Set<Component> dirtyComponents = new HashSet<Component>();
+    private Root root;
+
+    public static Logger getLogger() {
+        return Logger.getLogger(DirtyConnectorTracker.class.getName());
+    }
+
+    public DirtyConnectorTracker(Root root) {
+        this.root = root;
+    }
+
+    public void repaintRequested(RepaintRequestEvent event) {
+        markDirty((Component) event.getPaintable());
+    }
+
+    public void componentAttached(Component component) {
+        component.addListener(this);
+        markDirty(component);
+    }
+
+    private void markDirty(Component component) {
+        // TODO Remove debug info
+        if (!dirtyComponents.contains(component)) {
+            debug(component, "is now dirty");
+
+        }
+        dirtyComponents.add(component);
+    }
+
+    private void debug(Component component, String string) {
+        getLogger().info(getDebugInfo(component) + " " + string);
+    }
+
+    private void markClean(Component component) {
+        // TODO Remove debug info
+        if (dirtyComponents.contains(component)) {
+            debug(component, "is no longer dirty");
+        }
+        dirtyComponents.remove(component);
+
+        // TODO .... WTF ....
+        component.requestRepaintRequests();
+
+    }
+
+    private String getDebugInfo(Component component) {
+        String message = getObjectString(component);
+        if (component.getParent() != null) {
+            message += " (parent: " + getObjectString(component.getParent())
+                    + ")";
+        }
+        return message;
+    }
+
+    private String getObjectString(Object component) {
+        return component.getClass().getName() + "@"
+                + Integer.toHexString(component.hashCode());
+    }
+
+    public void componentDetached(Component component) {
+        component.removeListener(this);
+        markClean(component);
+    }
+
+    public void markAllComponentsDirty() {
+        markComponentsDirtyRecursively(root);
+        System.out.println("All components are now dirty");
+
+    }
+
+    public void markAllComponentsClean() {
+        dirtyComponents.clear();
+        System.out.println("All components are now clean");
+    }
+
+    private void markComponentsDirtyRecursively(Component c) {
+        markDirty(c);
+        if (c instanceof HasComponents) {
+            HasComponents container = (HasComponents) c;
+            for (Component child : container) {
+                markComponentsDirtyRecursively(child);
+            }
+        }
+
+    }
+
+    public Collection<Component> getDirtyComponents() {
+        return dirtyComponents;
+    }
+
+}
index 42d921d93fc7db15ff3d1eda64ec5435b268a7a4..802fc0a45fbc82778f55463c2932e9091c9e186f 100644 (file)
@@ -409,6 +409,9 @@ public abstract class Root extends AbstractComponentContainer implements
     /** Identifies the click event */
     private static final String CLICK_EVENT_ID = VView.CLICK_EVENT_ID;
 
+    private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
+            this);
+
     /**
      * Creates a new empty root without a caption. This root will have a
      * {@link VerticalLayout} with margins enabled as its content.
@@ -1567,4 +1570,17 @@ public abstract class Root extends AbstractComponentContainer implements
         // TODO How can a Root be invisible? What does it mean?
         return isVisible() && isEnabled();
     }
+
+    public DirtyConnectorTracker getDirtyConnectorTracker() {
+        return dirtyConnectorTracker;
+    }
+
+    public void componentAttached(Component component) {
+        getDirtyConnectorTracker().componentAttached(component);
+    }
+
+    public void componentDetached(Component component) {
+        getDirtyConnectorTracker().componentDetached(component);
+    }
+
 }