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;
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;
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();
+ }
+ }
+ }
+
+ }
}
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
*/
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.
*/
} 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
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
*/
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";
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";
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) {
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) {
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;
/* 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
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
*/
@SuppressWarnings("serial")
public abstract class AbstractCommunicationManager implements
- Paintable.RepaintRequestListener, PaintableIdMapper, Serializable {
+ Paintable.RepaintRequestListener, Serializable {
private static final String DASHDASH = "--";
// 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;
private int maxInactiveInterval;
+ private Connector highlightedConnector;
+
/**
* TODO New constructor - document me!
*
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();
*/
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
*/
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
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);
}
}
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());
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);
// 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);
}
}
}
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\" : {");
}
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;
}
}
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
* @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;
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.
*
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);
* 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());
}
}
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);
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);
}
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.
* 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);
+ // }
}
/**
* @param paintable
*/
private void paintablePainted(Paintable paintable) {
- dirtyPaintables.remove(paintable);
- paintable.requestRepaintRequests();
+ // dirtyPaintables.remove(paintable);
+ // paintable.requestRepaintRequests();
}
/**
}
}
- /**
- * 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 {
}
- 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
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("{");
return b;
}
}
+
+ @Deprecated
+ public String getPaintableId(Paintable paintable) {
+ if (paintable instanceof Connector) {
+ return ((Connector) paintable).getConnectorId();
+ }
+ throw new RuntimeException("Paintable " + paintable
+ + " must implement Connector");
+ }
}
--- /dev/null
+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();
+
+}
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;
// 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() {
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
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;
/**
/**
* Handles file upload request submitted via Upload component.
*
+ * @param application
+ *
* @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
*
* @param request
* @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
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
}
@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);
}
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
* 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) {
}
@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());
}
}
clientJSON.write("{");
Component parent = component.getParent();
- String paintableId = communicationManager.getPaintableId(component);
+ String paintableId = component.getConnectorId();
clientJSON.print("id:\"" + paintableId + "\"");
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());
}
public boolean isEnabled() {
- return true;
+ return isConnectorEnabled();
}
public boolean isImmediate() {
}
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;
+ }
}
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;
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);
*
* @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)) {
} 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;
}
}
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;
}
*
* @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);
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));
}
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();
}
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?
}
private static Object decodeObject(String type,
- JSONObject serializedObject, PaintableIdMapper idMapper)
+ JSONObject serializedObject, Application application)
throws JSONException {
Class<?> cls;
JSONArray encodedObject = serializedObject
.getJSONArray(fieldName);
pd.getWriteMethod().invoke(decodedObject,
- decode(encodedObject, idMapper));
+ decode(encodedObject, application));
}
return decodedObject;
}
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;
}
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;
private final Collection<Paintable> paintedComponents = new HashSet<Paintable>();
- private Collection<Paintable> identifiersCreatedDueRefPaint;
+ // private Collection<Paintable> identifiersCreatedDueRefPaint;
private Collection<Paintable> deferredPaintables;
*/
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();
}
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);
}
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>>();
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 }
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import javax.portlet.MimeResponse;
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;
/**
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();
+ }
+ }
}
}
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);
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);
*/
private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
+ private String connectorId;
+
/* Constructor */
/**
* interface.
*/
public void attach() {
+ getRoot().componentAttached(this);
requestRepaint();
if (!getState().isVisible()) {
/*
// compiler happy
actionManager.setViewer((Root) null);
}
+ getRoot().componentDetached(this);
}
/**
}
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;
}
}
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;
/**
* @VERSION@
* @since 3.0
*/
-public interface Component extends Connector, Paintable, VariableOwner,
+public interface Component extends ClientConnector, Paintable, VariableOwner,
Sizeable, Serializable, RpcTarget {
/**
--- /dev/null
+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;
+ }
+
+}
/** 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.
// 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);
+ }
+
}