Browse Source

Moved Connector -> Connector Id mapping to AbstractComponent

Moved Connector Id -> Connector mapping to Application
Moved dirty connector tracking to Root
Removed adding of
tags/7.0.0.alpha2
Artur Signell 12 years ago
parent
commit
36dca64414

+ 66
- 0
src/com/vaadin/Application.java View File

import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import com.vaadin.terminal.WrappedRequest.BrowserDetails; import com.vaadin.terminal.WrappedRequest.BrowserDetails;
import com.vaadin.terminal.WrappedResponse; import com.vaadin.terminal.WrappedResponse;
import com.vaadin.terminal.gwt.client.ApplicationConnection; 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.AbstractApplicationServlet;
import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent; import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
import com.vaadin.terminal.gwt.server.WebApplicationContext; import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField; import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Component;
import com.vaadin.ui.Root; import com.vaadin.ui.Root;
import com.vaadin.ui.Table; import com.vaadin.ui.Table;
import com.vaadin.ui.Window; import com.vaadin.ui.Window;
public Collection<Root> getRoots() { public Collection<Root> getRoots() {
return Collections.unmodifiableCollection(roots.values()); 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();
}
}
}

}
} }

+ 0
- 28
src/com/vaadin/terminal/Paintable.java View File



import java.io.Serializable; import java.io.Serializable;
import java.util.EventObject; 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 * Interface implemented by all classes that can be painted. Classes
*/ */
public void paint(PaintTarget target) throws PaintException; 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. * Requests that the paintable should be repainted as soon as possible.
*/ */

+ 1
- 1
src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java View File

} else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) { } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) {
// TODO handle properly // TODO handle properly
val = Boolean.valueOf(String.valueOf(value)); 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()); val = idMapper.getConnector(((JSONString) value).stringValue());
} else { } else {
// object, class name as type // object, class name as type

+ 9
- 9
src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java View File

import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue; import com.google.gwt.json.client.JSONValue;
import com.vaadin.terminal.gwt.client.ApplicationConnection; 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.ConnectorMap;
import com.vaadin.terminal.gwt.client.ServerConnector;


/** /**
* Encoder for converting RPC parameters and other values to JSON for transfer * Encoder for converting RPC parameters and other values to JSON for transfer
*/ */
public class JsonEncoder { 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_BOOLEAN = "b";
public static final String VTYPE_DOUBLE = "d"; public static final String VTYPE_DOUBLE = "d";
public static final String VTYPE_FLOAT = "f"; public static final String VTYPE_FLOAT = "f";
public static final String VTYPE_INTEGER = "i"; public static final String VTYPE_INTEGER = "i";
public static final String VTYPE_STRING = "s"; public static final String VTYPE_STRING = "s";
public static final String VTYPE_ARRAY = "a"; 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_MAP = "m";
public static final String VTYPE_LIST = "L"; public static final String VTYPE_LIST = "L";
public static final String VTYPE_NULL = "n"; public static final String VTYPE_NULL = "n";
jsonMap.put(mapKey, encode(mapValue, connectorMap, connection)); jsonMap.put(mapKey, encode(mapValue, connectorMap, connection));
} }
return combineTypeAndValue(VTYPE_MAP, jsonMap); 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 { } else {
String transportType = getTransportType(value); String transportType = getTransportType(value);
if (transportType != null) { if (transportType != null) {
return VTYPE_NULL; return VTYPE_NULL;
} else if (value instanceof String) { } else if (value instanceof String) {
return VTYPE_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) { } else if (value instanceof Boolean) {
return VTYPE_BOOLEAN; return VTYPE_BOOLEAN;
} else if (value instanceof Integer) { } else if (value instanceof Integer) {

+ 1
- 1
src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java View File

ENTER, LEAVE, OVER, DROP ENTER, LEAVE, OVER, DROP
} }


private static final String DD_SERVICE = "DD";
public static final String DD_SERVICE = "DD";


private static VDragAndDropManager instance; private static VDragAndDropManager instance;
private HandlerRegistration handlerRegistration; private HandlerRegistration handlerRegistration;

+ 2
- 1
src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java View File



/* Handle the request */ /* Handle the request */
if (requestType == RequestType.FILE_UPLOAD) { if (requestType == RequestType.FILE_UPLOAD) {
applicationManager.handleFileUpload(request, response);
applicationManager.handleFileUpload(application, request,
response);
return; return;
} else if (requestType == RequestType.UIDL) { } else if (requestType == RequestType.UIDL) {
// Handles AJAX UIDL requests // Handles AJAX UIDL requests

+ 238
- 423
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java View File

import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField; import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Component; import com.vaadin.ui.Component;
import com.vaadin.ui.DirtyConnectorTracker;
import com.vaadin.ui.HasComponents; import com.vaadin.ui.HasComponents;
import com.vaadin.ui.Panel; import com.vaadin.ui.Panel;
import com.vaadin.ui.Root; import com.vaadin.ui.Root;
import com.vaadin.ui.Window;


/** /**
* This is a common base class for the server-side implementations of the * This is a common base class for the server-side implementations of the
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public abstract class AbstractCommunicationManager implements public abstract class AbstractCommunicationManager implements
Paintable.RepaintRequestListener, PaintableIdMapper, Serializable {
Paintable.RepaintRequestListener, Serializable {


private static final String DASHDASH = "--"; private static final String DASHDASH = "--";




// cannot combine with paint queue: // cannot combine with paint queue:
// this can contain dirty components from any Root // 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 // queue used during painting to keep track of what still needs to be
// painted within the Root being painted // 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 idSequence = 0;




private int maxInactiveInterval; private int maxInactiveInterval;


private Connector highlightedConnector;

/** /**
* TODO New constructor - document me! * TODO New constructor - document me!
* *


private static final String GET_PARAM_HIGHLIGHT_COMPONENT = "highlightComponent"; private static final String GET_PARAM_HIGHLIGHT_COMPONENT = "highlightComponent";


private Paintable highLightedPaintable;

private static String readLine(InputStream stream) throws IOException { private static String readLine(InputStream stream) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream(); ByteArrayOutputStream bout = new ByteArrayOutputStream();
int readByte = stream.read(); int readByte = stream.read();
*/ */
protected void doHandleSimpleMultipartFileUpload(WrappedRequest request, protected void doHandleSimpleMultipartFileUpload(WrappedRequest request,
WrappedResponse response, StreamVariable streamVariable, WrappedResponse response, StreamVariable streamVariable,
String variableName, VariableOwner owner, String boundary)
String variableName, Connector owner, String boundary)
throws IOException { throws IOException {
// multipart parsing, supports only one file for request, but that is // multipart parsing, supports only one file for request, but that is
// fine for our current terminal // fine for our current terminal
*/ */
protected void doHandleXhrFilePost(WrappedRequest request, protected void doHandleXhrFilePost(WrappedRequest request,
WrappedResponse response, StreamVariable streamVariable, WrappedResponse response, StreamVariable streamVariable,
String variableName, VariableOwner owner, int contentLength)
String variableName, Connector owner, int contentLength)
throws IOException { throws IOException {


// These are unknown in filexhr ATM, maybe add to Accept header that // These are unknown in filexhr ATM, maybe add to Accept header that
if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) { if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) {
String pid = request String pid = request
.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT); .getParameter(GET_PARAM_HIGHLIGHT_COMPONENT);
highLightedPaintable = idPaintableMap.get(pid);
highlightPaintable(highLightedPaintable);
highlightedConnector = root.getApplication().getConnector(pid);
highlightConnector(highlightedConnector);
} }
} }




paintAfterVariableChanges(request, response, callback, repaintAll, paintAfterVariableChanges(request, response, callback, repaintAll,
outWriter, root, analyzeLayouts); outWriter, root, analyzeLayouts);
postPaint(root);
} }


outWriter.close(); outWriter.close();
requestThemeName = null; 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(); StringBuilder sb = new StringBuilder();
sb.append("*** Debug details of a component: *** \n"); sb.append("*** Debug details of a component: *** \n");
sb.append("Type: "); 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("\nId:");
sb.append(paintableIdMap.get(component));
sb.append(highlightedConnector.getConnectorId());
if (component.getCaption() != null) { if (component.getCaption() != null) {
sb.append("\nCaption:"); sb.append("\nCaption:");
sb.append(component.getCaption()); sb.append(component.getCaption());
final PrintWriter outWriter, Root root, boolean analyzeLayouts) final PrintWriter outWriter, Root root, boolean analyzeLayouts)
throws PaintException, IOException { throws PaintException, IOException {


if (repaintAll) {
makeAllPaintablesDirty(root);
}

// Removes application if it has stopped during variable changes // Removes application if it has stopped during variable changes
if (!application.isRunning()) { if (!application.isRunning()) {
endApplication(request, response, application); endApplication(request, response, application);


// for internal use by JsonPaintTarget // for internal use by JsonPaintTarget
public void queuePaintable(Paintable paintable) { 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, public void writeUidlResponse(boolean repaintAll,
final PrintWriter outWriter, Root root, boolean analyzeLayouts) final PrintWriter outWriter, Root root, boolean analyzeLayouts)
throws PaintException { throws PaintException {
ArrayList<Paintable> paintables = null;
ArrayList<Connector> dirtyVisibleConnectors = new ArrayList<Connector>();
Application application = root.getApplication();
// Paints components // Paints components
DirtyConnectorTracker rootConnectorTracker = root
.getDirtyConnectorTracker();
System.out.println("* Creating response to client");
if (repaintAll) { 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 // Reset sent locales
locales = null; locales = null;
requireLocale(application.getLocale().toString()); requireLocale(application.getLocale().toString());


} else { } 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? // 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\":["); outWriter.print("\"changes\":[");


List<InvalidLayout> invalidComponentRelativeSizes = null; List<InvalidLayout> invalidComponentRelativeSizes = null;


JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter, JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter,
!repaintAll); !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 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(); paintTarget.close();
outWriter.print("], "); // close changes 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 { try {
connectorTypes.put(paintableId, connectorType);
JSONArray stateJsonArray = JsonCodec.encode(state,
application);
sharedStates
.put(connector.getConnectorId(), stateJsonArray);
} catch (JSONException e) { } catch (JSONException e) {
throw new PaintException( 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) { } catch (JSONException e) {
throw new PaintException( 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()); + 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.print("\"meta\" : {");
} }
outWriter.write("]"); outWriter.write("]");
} }
if (highLightedPaintable != null) {
if (highlightedConnector != null) {
outWriter.write(", \"hl\":\""); outWriter.write(", \"hl\":\"");
outWriter.write(paintableIdMap.get(highLightedPaintable));
outWriter.write(highlightedConnector.getConnectorId());
outWriter.write("\""); outWriter.write("\"");
highLightedPaintable = null;
highlightedConnector = null;
} }
} }


Collection<Class<? extends Paintable>> usedPaintableTypes = paintTarget Collection<Class<? extends Paintable>> usedPaintableTypes = paintTarget
.getUsedPaintableTypes(); .getUsedPaintableTypes();
boolean typeMappingsOpen = false; boolean typeMappingsOpen = false;
ClientCache clientCache = getClientCache(root);

for (Class<? extends Paintable> class1 : usedPaintableTypes) { for (Class<? extends Paintable> class1 : usedPaintableTypes) {
if (clientCache.cache(class1)) { if (clientCache.cache(class1)) {
// client does not know the mapping key for this type, send // client does not know the mapping key for this type, send
* @return ordered list of pending RPC calls * @return ordered list of pending RPC calls
*/ */
private List<ClientMethodInvocation> collectPendingRpcCalls( private List<ClientMethodInvocation> collectPendingRpcCalls(
LinkedList<Paintable> rpcPendingQueue) {
LinkedList<ClientConnector> rpcPendingQueue) {
List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
for (Paintable paintable : rpcPendingQueue) {
List<ClientMethodInvocation> paintablePendingRpc = paintable
for (ClientConnector connector : rpcPendingQueue) {
List<ClientMethodInvocation> paintablePendingRpc = connector
.retrievePendingRpcCalls(); .retrievePendingRpcCalls();
if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) { if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) {
List<ClientMethodInvocation> oldPendingRpc = pendingInvocations; List<ClientMethodInvocation> oldPendingRpc = pendingInvocations;
return requestThemeName; 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. * Returns false if the cross site request forgery protection is turned off.
* *
if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE
.equals(interfaceName)) { .equals(interfaceName)) {
// handle other RPC calls than variable changes // handle other RPC calls than variable changes
applyInvocation(invocation);
applyInvocation(app, invocation);
continue; 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) { if (owner != null && connectorEnabled) {
VariableChange change = new VariableChange(invocation); VariableChange change = new VariableChange(invocation);
* Execute an RPC call from the client by finding its target and letting the * Execute an RPC call from the client by finding its target and letting the
* RPC mechanism call the correct method for it. * RPC mechanism call the correct method for it.
* *
* @param app
*
* @param invocation * @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 { } else {
// TODO better exception? // TODO better exception?
throw new RuntimeException("No RPC target for paintable "
throw new RuntimeException("No RPC target for connector "
+ invocation.getConnectorId()); + invocation.getConnectorId());
} }
} }
Object[] parameters = new Object[parametersJson.length()]; Object[] parameters = new Object[parametersJson.length()];
for (int j = 0; j < parametersJson.length(); ++j) { for (int j = 0; j < parametersJson.length(); ++j) {
parameters[j] = JsonCodec.decode( parameters[j] = JsonCodec.decode(
parametersJson.getJSONArray(j), this);
parametersJson.getJSONArray(j), application);
} }
MethodInvocation invocation = new MethodInvocation(connectorId, MethodInvocation invocation = new MethodInvocation(connectorId,
interfaceName, methodName, parameters); interfaceName, methodName, parameters);
owner.changeVariables(source, m); 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 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) { if (dragAndDropService == null) {
dragAndDropService = new DragAndDropService(this); dragAndDropService = new DragAndDropService(this);
} }
outWriter.print("for(;;);[{"); 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 * Returns dirty components which are in given window. Components in an
* invisible subtrees are omitted. * invisible subtrees are omitted.
* root window for which dirty components is to be fetched * root window for which dirty components is to be fetched
* @return * @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) * @see com.vaadin.terminal.Paintable.RepaintRequestListener#repaintRequested(com.vaadin.terminal.Paintable.RepaintRequestEvent)
*/ */
public void repaintRequested(RepaintRequestEvent event) { 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 * @param paintable
*/ */
private void paintablePainted(Paintable 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 protected class InvalidUIDLSecurityKeyException extends
GeneralSecurityException { 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 * Gets the bootstrap handler that should be used for generating the pages
protected String getInitialUIDL(WrappedRequest request, Root root) protected String getInitialUIDL(WrappedRequest request, Root root)
throws PaintException { throws PaintException {
// TODO maybe unify writeUidlResponse()? // TODO maybe unify writeUidlResponse()?
makeAllPaintablesDirty(root);
StringWriter sWriter = new StringWriter(); StringWriter sWriter = new StringWriter();
PrintWriter pWriter = new PrintWriter(sWriter); PrintWriter pWriter = new PrintWriter(sWriter);
pWriter.print("{"); pWriter.print("{");
return b; return b;
} }
} }

@Deprecated
public String getPaintableId(Paintable paintable) {
if (paintable instanceof Connector) {
return ((Connector) paintable).getConnectorId();
}
throw new RuntimeException("Paintable " + paintable
+ " must implement Connector");
}
} }

+ 19
- 0
src/com/vaadin/terminal/gwt/server/ClientConnector.java View File

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();

}

+ 7
- 9
src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java View File



import java.io.Serializable; import java.io.Serializable;


import com.vaadin.terminal.Paintable;

/** /**
* Internal class for keeping track of pending server to client method * Internal class for keeping track of pending server to client method
* invocations for a Paintable.
* invocations for a Connector.
* *
* @since 7.0 * @since 7.0
*/ */
public class ClientMethodInvocation implements Serializable, public class ClientMethodInvocation implements Serializable,
Comparable<ClientMethodInvocation> { Comparable<ClientMethodInvocation> {
private final Paintable paintable;
private final ClientConnector connector;
private final String interfaceName; private final String interfaceName;
private final String methodName; private final String methodName;
private final Object[] parameters; private final Object[] parameters;
// TODO may cause problems when clustering etc. // TODO may cause problems when clustering etc.
private static long counter = 0; 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.interfaceName = interfaceName;
this.methodName = methodName; this.methodName = methodName;
this.parameters = (null != parameters) ? parameters : new Object[0]; this.parameters = (null != parameters) ? parameters : new Object[0];
sequenceNumber = ++counter; sequenceNumber = ++counter;
} }


public Paintable getPaintable() {
return paintable;
public ClientConnector getConnector() {
return connector;
} }


public String getInterfaceName() { public String getInterfaceName() {

+ 29
- 20
src/com/vaadin/terminal/gwt/server/CommunicationManager.java View File

import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;




import com.vaadin.Application; import com.vaadin.Application;
import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.Paintable;
import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.StreamVariable;
import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.WrappedRequest; import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.WrappedResponse; import com.vaadin.terminal.WrappedResponse;
import com.vaadin.ui.Component;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.ui.Root; import com.vaadin.ui.Root;


/** /**
/** /**
* Handles file upload request submitted via Upload component. * Handles file upload request submitted via Upload component.
* *
* @param application
*
* @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable) * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
* *
* @param request * @param request
* @throws IOException * @throws IOException
* @throws InvalidUIDLSecurityKeyException * @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 * URI pattern: APP/UPLOAD/[PID]/[NAME]/[SECKEY] See #createReceiverUrl
String secKey = streamVariableToSeckey.get(streamVariable); String secKey = streamVariableToSeckey.get(streamVariable);
if (secKey.equals(parts[2])) { if (secKey.equals(parts[2])) {


VariableOwner source = getVariableOwner(paintableId);
Connector source = getConnector(application, paintableId);
String contentType = request.getContentType(); String contentType = request.getContentType();
if (contentType.contains("boundary")) { if (contentType.contains("boundary")) {
// Multipart requests contain boundary string // Multipart requests contain boundary string
} }


@Override @Override
protected void unregisterPaintable(Component p) {
/* Cleanup possible receivers */
protected void postPaint(Root root) {
super.postPaint(root);

Application application = root.getApplication();
if (pidToNameToStreamVariable != null) { 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; private Map<StreamVariable, String> streamVariableToSeckey;


@Override @Override
String getStreamVariableTargetUrl(VariableOwner owner, String name,
String getStreamVariableTargetUrl(Connector owner, String name,
StreamVariable value) { StreamVariable value) {
/* /*
* We will use the same APP/* URI space as ApplicationResources but * We will use the same APP/* URI space as ApplicationResources but
* NAME and PID from URI forms a key to fetch StreamVariable when * NAME and PID from URI forms a key to fetch StreamVariable when
* handling post * handling post
*/ */
String paintableId = getPaintableId((Paintable) owner);
String paintableId = owner.getConnectorId();
String key = paintableId + "/" + name; String key = paintableId + "/" + name;


if (pidToNameToStreamVariable == null) { if (pidToNameToStreamVariable == null) {
} }


@Override @Override
protected void cleanStreamVariable(VariableOwner owner, String name) {
protected void cleanStreamVariable(Connector owner, String name) {
Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable
.get(getPaintableId((Paintable) owner));
.get(owner.getConnectorId());
nameToStreamVar.remove("name"); nameToStreamVar.remove("name");
if (nameToStreamVar.isEmpty()) { if (nameToStreamVar.isEmpty()) {
pidToNameToStreamVariable.remove(getPaintableId((Paintable) owner));
pidToNameToStreamVariable.remove(owner.getConnectorId());
} }
} }



+ 1
- 1
src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java View File

clientJSON.write("{"); clientJSON.write("{");


Component parent = component.getParent(); Component parent = component.getParent();
String paintableId = communicationManager.getPaintableId(component);
String paintableId = component.getConnectorId();


clientJSON.print("id:\"" + paintableId + "\""); clientJSON.print("id:\"" + paintableId + "\"");



+ 19
- 2
src/com/vaadin/terminal/gwt/server/DragAndDropService.java View File

import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.VariableOwner; 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.terminal.gwt.client.ui.dd.VDragAndDropManager.DragEventType;
import com.vaadin.ui.Component; import com.vaadin.ui.Component;


public class DragAndDropService implements VariableOwner {
public class DragAndDropService implements VariableOwner, Connector {


private static final Logger logger = Logger private static final Logger logger = Logger
.getLogger(DragAndDropService.class.getName()); .getLogger(DragAndDropService.class.getName());
} }


public boolean isEnabled() { public boolean isEnabled() {
return true;
return isConnectorEnabled();
} }


public boolean isImmediate() { public boolean isImmediate() {
} }
return false; 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;
}
} }

+ 44
- 43
src/com/vaadin/terminal/gwt/server/JsonCodec.java View File

import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;


import com.vaadin.Application;
import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONArray;
import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONException;
import com.vaadin.external.json.JSONObject; import com.vaadin.external.json.JSONObject;


static { static {
registerType(String.class, JsonEncoder.VTYPE_STRING); 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(Boolean.class, JsonEncoder.VTYPE_BOOLEAN);
registerType(Integer.class, JsonEncoder.VTYPE_INTEGER); registerType(Integer.class, JsonEncoder.VTYPE_INTEGER);
registerType(Float.class, JsonEncoder.VTYPE_FLOAT); registerType(Float.class, JsonEncoder.VTYPE_FLOAT);
* *
* @param value * @param value
* JSON array with two elements * JSON array with two elements
* @param idMapper
* @param application
* mapper between paintable ID and {@link Paintable} objects * mapper between paintable ID and {@link Paintable} objects
* @return converted value (does not contain JSON types) * @return converted value (does not contain JSON types)
* @throws JSONException * @throws JSONException
* if the conversion fails * if the conversion fails
*/ */
public static Object decode(JSONArray value, PaintableIdMapper idMapper)
public static Object decode(JSONArray value, Application application)
throws JSONException { 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, private static Object decodeVariableValue(String variableType,
Object value, PaintableIdMapper idMapper) throws JSONException {
Object value, Application application) throws JSONException {
Object val = null; Object val = null;
// TODO type checks etc. // TODO type checks etc.
if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) { if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) {
val = decodeArray((JSONArray) value, idMapper);
val = decodeArray((JSONArray) value, application);
} else if (JsonEncoder.VTYPE_LIST.equals(variableType)) { } else if (JsonEncoder.VTYPE_LIST.equals(variableType)) {
val = decodeList((JSONArray) value, idMapper);
val = decodeList((JSONArray) value, application);
} else if (JsonEncoder.VTYPE_MAP.equals(variableType)) { } else if (JsonEncoder.VTYPE_MAP.equals(variableType)) {
val = decodeMap((JSONObject) value, idMapper);
val = decodeMap((JSONObject) value, application);
} else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) { } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) {
val = decodeStringArray((JSONArray) value); val = decodeStringArray((JSONArray) value);
} else if (JsonEncoder.VTYPE_STRING.equals(variableType)) { } else if (JsonEncoder.VTYPE_STRING.equals(variableType)) {
} else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) { } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) {
// TODO handle properly // TODO handle properly
val = Boolean.valueOf(String.valueOf(value)); 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)) { } else if (JsonEncoder.VTYPE_NULL.equals(variableType)) {
val = null; val = null;
} else { } else {
// Try to decode object using fields // Try to decode object using fields
return decodeObject(variableType, (JSONObject) value, idMapper);
return decodeObject(variableType, (JSONObject) value, application);


} }


return val; 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>(); HashMap<String, Object> map = new HashMap<String, Object>();
Iterator<String> it = jsonMap.keys(); Iterator<String> it = jsonMap.keys();
while (it.hasNext()) { while (it.hasNext()) {
String key = it.next(); String key = it.next();
map.put(key, decode(jsonMap.getJSONArray(key), idMapper));
map.put(key, decode(jsonMap.getJSONArray(key), application));
} }
return map; return map;
} }
} }


private static Object decodeArray(JSONArray jsonArray, 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()]); 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>(); List<Object> list = new ArrayList<Object>();
for (int i = 0; i < jsonArray.length(); ++i) { for (int i = 0; i < jsonArray.length(); ++i) {
// each entry always has two elements: type and value // each entry always has two elements: type and value
JSONArray entryArray = jsonArray.getJSONArray(i); JSONArray entryArray = jsonArray.getJSONArray(i);
list.add(decode(entryArray, idMapper));
list.add(decode(entryArray, application));
} }
return list; return list;
} }
* *
* @param value * @param value
* value to convert * value to convert
* @param idMapper
* @param application
* mapper between paintable ID and {@link Paintable} objects * mapper between paintable ID and {@link Paintable} objects
* @return JSON representation of the value * @return JSON representation of the value
* @throws JSONException * @throws JSONException
* if encoding a value fails (e.g. NaN or infinite number) * 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 { throws JSONException {
return encode(value, null, idMapper);
return encode(value, null, application);
} }


public static JSONArray encode(Object value, Class<?> valueType, public static JSONArray encode(Object value, Class<?> valueType,
PaintableIdMapper idMapper) throws JSONException {
Application application) throws JSONException {


if (null == value) { if (null == value) {
return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL); return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL);
return combineTypeAndValue(getTransportType(value), value); return combineTypeAndValue(getTransportType(value), value);
} else if (value instanceof List) { } else if (value instanceof List) {
List list = (List) value; List list = (List) value;
JSONArray jsonArray = encodeList(list, idMapper);
JSONArray jsonArray = encodeList(list, application);
return combineTypeAndValue(JsonEncoder.VTYPE_LIST, jsonArray); return combineTypeAndValue(JsonEncoder.VTYPE_LIST, jsonArray);
} else if (value instanceof Object[]) { } else if (value instanceof Object[]) {
Object[] array = (Object[]) value; Object[] array = (Object[]) value;
JSONArray jsonArray = encodeArrayContents(array, idMapper);
JSONArray jsonArray = encodeArrayContents(array, application);
return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray); return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray);
} else if (value instanceof Map) { } else if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value; Map<String, Object> map = (Map<String, Object>) value;
JSONObject jsonMap = encodeMapContents(map, idMapper);
JSONObject jsonMap = encodeMapContents(map, application);
return combineTypeAndValue(JsonEncoder.VTYPE_MAP, jsonMap); 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) { } else if (getTransportType(value) != null) {
return combineTypeAndValue(getTransportType(value), return combineTypeAndValue(getTransportType(value),
String.valueOf(value)); String.valueOf(value));
} }


return combineTypeAndValue(valueType.getCanonicalName(), 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 { throws JSONException {
JSONObject jsonMap = new JSONObject(); JSONObject jsonMap = new JSONObject();


} }
Method getterMethod = pd.getReadMethod(); Method getterMethod = pd.getReadMethod();
Object fieldValue = getterMethod.invoke(value, (Object[]) null); Object fieldValue = getterMethod.invoke(value, (Object[]) null);
jsonMap.put(fieldName, encode(fieldValue, fieldType, idMapper));
jsonMap.put(fieldName,
encode(fieldValue, fieldType, application));
} }
} catch (Exception e) { } catch (Exception e) {
// TODO: Should exceptions be handled in a different way? // TODO: Should exceptions be handled in a different way?
} }


private static Object decodeObject(String type, private static Object decodeObject(String type,
JSONObject serializedObject, PaintableIdMapper idMapper)
JSONObject serializedObject, Application application)
throws JSONException { throws JSONException {


Class<?> cls; Class<?> cls;
JSONArray encodedObject = serializedObject JSONArray encodedObject = serializedObject
.getJSONArray(fieldName); .getJSONArray(fieldName);
pd.getWriteMethod().invoke(decodedObject, pd.getWriteMethod().invoke(decodedObject,
decode(encodedObject, idMapper));
decode(encodedObject, application));
} }


return decodedObject; return decodedObject;
} }


private static JSONArray encodeArrayContents(Object[] array, private static JSONArray encodeArrayContents(Object[] array,
PaintableIdMapper idMapper) throws JSONException {
Application application) throws JSONException {
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
for (Object o : array) { for (Object o : array) {
// TODO handle object graph loops? // TODO handle object graph loops?
jsonArray.put(encode(o, idMapper));
jsonArray.put(encode(o, application));
} }
return jsonArray; return jsonArray;
} }


private static JSONArray encodeList(List list, PaintableIdMapper idMapper)
private static JSONArray encodeList(List list, Application application)
throws JSONException { throws JSONException {
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
for (Object o : list) { for (Object o : list) {
// TODO handle object graph loops? // TODO handle object graph loops?
jsonArray.put(encode(o, idMapper));
jsonArray.put(encode(o, application));
} }
return jsonArray; return jsonArray;
} }


private static JSONObject encodeMapContents(Map<String, Object> map, private static JSONObject encodeMapContents(Map<String, Object> map,
PaintableIdMapper idMapper) throws JSONException {
Application application) throws JSONException {
JSONObject jsonMap = new JSONObject(); JSONObject jsonMap = new JSONObject();
for (String mapKey : map.keySet()) { for (String mapKey : map.keySet()) {
// TODO handle object graph loops? // TODO handle object graph loops?
Object mapValue = map.get(mapKey); Object mapValue = map.get(mapKey);
jsonMap.put(mapKey, encode(mapValue, idMapper));
jsonMap.put(mapKey, encode(mapValue, application));
} }
return jsonMap; return jsonMap;
} }

+ 50
- 52
src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java View File

import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.StreamVariable;
import com.vaadin.terminal.ThemeResource; import com.vaadin.terminal.ThemeResource;
import com.vaadin.terminal.VariableOwner; import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.ui.Alignment; import com.vaadin.ui.Alignment;
import com.vaadin.ui.ClientWidget; import com.vaadin.ui.ClientWidget;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomLayout; import com.vaadin.ui.CustomLayout;
import com.vaadin.ui.Root; import com.vaadin.ui.Root;




private final Collection<Paintable> paintedComponents = new HashSet<Paintable>(); private final Collection<Paintable> paintedComponents = new HashSet<Paintable>();


private Collection<Paintable> identifiersCreatedDueRefPaint;
// private Collection<Paintable> identifiersCreatedDueRefPaint;


private Collection<Paintable> deferredPaintables; private Collection<Paintable> deferredPaintables;


*/ */
public PaintStatus startPaintable(Paintable paintable, String tagName) public PaintStatus startPaintable(Paintable paintable, String tagName)
throws PaintException { throws PaintException {
boolean topLevelPaintable = openPaintables.isEmpty();

System.out.println("startPaintable for "
+ paintable.getClass().getName() + "@"
+ Integer.toHexString(paintable.hashCode()));
startTag(tagName, true); 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); final String id = manager.getPaintableId(paintable);
paintable.addListener(manager); paintable.addListener(manager);
addAttribute("id", id); addAttribute("id", id);


// queue for painting later if already painting a paintable // 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 // 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; 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 { public void endPaintable(Paintable paintable) throws PaintException {
Paintable openPaintable = openPaintables.peek(); Paintable openPaintable = openPaintables.peek();
if (paintable != openPaintable) { if (paintable != openPaintable) {
throw new PaintException("Invalid UIDL: closing wrong paintable: '" 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 // remove paintable from the stack
openPaintables.pop(); openPaintables.pop();
} }


public String getPaintIdentifier(Paintable paintable) throws PaintException { 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 manager.getPaintableId(paintable);
} }


return usedResources; 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>>(); 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, public void addVariable(VariableOwner owner, String name,
StreamVariable value) throws PaintException { StreamVariable value) throws PaintException {
String url = manager.getStreamVariableTargetUrl(owner, name, value);
String url = manager.getStreamVariableTargetUrl((Connector) owner,
name, value);
if (url != null) { if (url != null) {
addVariable(owner, name, url); addVariable(owner, name, url);
} // else { //NOP this was just a cleanup by component } } // else { //NOP this was just a cleanup by component }

+ 27
- 18
src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java View File

import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;


import javax.portlet.MimeResponse; import javax.portlet.MimeResponse;
import com.vaadin.external.json.JSONObject; import com.vaadin.external.json.JSONObject;
import com.vaadin.terminal.DeploymentConfiguration; import com.vaadin.terminal.DeploymentConfiguration;
import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.Paintable;
import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.StreamVariable;
import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.WrappedRequest; import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.WrappedResponse; import com.vaadin.terminal.WrappedResponse;
import com.vaadin.ui.Component;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.ui.Root; import com.vaadin.ui.Root;


/** /**
String contentType = request.getContentType(); String contentType = request.getContentType();
String name = request.getParameter("name"); String name = request.getParameter("name");
String ownerId = request.getParameter("rec-owner"); 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")) { if (contentType.contains("boundary")) {
doHandleSimpleMultipartFileUpload(request, response, doHandleSimpleMultipartFileUpload(request, response,
streamVariable, name, variableOwner,
streamVariable, name, owner,
contentType.split("boundary=")[1]); contentType.split("boundary=")[1]);
} else { } else {
doHandleXhrFilePost(request, response, streamVariable, name,
variableOwner, request.getContentLength());
doHandleXhrFilePost(request, response, streamVariable, name, owner,
request.getContentLength());
} }


} }


@Override @Override
protected void unregisterPaintable(Component p) {
super.unregisterPaintable(p);
protected void postPaint(Root root) {
super.postPaint(root);

Application application = root.getApplication();
if (ownerToNameToStreamVariable != null) { 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; currentUidlResponse = null;
} }


private Map<VariableOwner, Map<String, StreamVariable>> ownerToNameToStreamVariable;
private Map<Connector, Map<String, StreamVariable>> ownerToNameToStreamVariable;


@Override @Override
String getStreamVariableTargetUrl(VariableOwner owner, String name,
String getStreamVariableTargetUrl(Connector owner, String name,
StreamVariable value) { StreamVariable value) {
if (ownerToNameToStreamVariable == null) { if (ownerToNameToStreamVariable == null) {
ownerToNameToStreamVariable = new HashMap<VariableOwner, Map<String, StreamVariable>>();
ownerToNameToStreamVariable = new HashMap<Connector, Map<String, StreamVariable>>();
} }
Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable
.get(owner); .get(owner);
ResourceURL resurl = currentUidlResponse.createResourceURL(); ResourceURL resurl = currentUidlResponse.createResourceURL();
resurl.setResourceID("UPLOAD"); resurl.setResourceID("UPLOAD");
resurl.setParameter("name", name); resurl.setParameter("name", name);
resurl.setParameter("rec-owner", getPaintableId((Paintable) owner));
resurl.setParameter("rec-owner", owner.getConnectorId());
resurl.setProperty("name", name); resurl.setProperty("name", name);
resurl.setProperty("rec-owner", getPaintableId((Paintable) owner));
resurl.setProperty("rec-owner", owner.getConnectorId());
return resurl.toString(); return resurl.toString();
} }


@Override @Override
protected void cleanStreamVariable(VariableOwner owner, String name) {
protected void cleanStreamVariable(Connector owner, String name) {
Map<String, StreamVariable> map = ownerToNameToStreamVariable Map<String, StreamVariable> map = ownerToNameToStreamVariable
.get(owner); .get(owner);
map.remove(name); map.remove(name);

+ 12
- 2
src/com/vaadin/ui/AbstractComponent.java View File

*/ */
private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();


private String connectorId;

/* Constructor */ /* Constructor */


/** /**
* interface. * interface.
*/ */
public void attach() { public void attach() {
getRoot().componentAttached(this);
requestRepaint(); requestRepaint();
if (!getState().isVisible()) { if (!getState().isVisible()) {
/* /*
// compiler happy // compiler happy
actionManager.setViewer((Root) null); actionManager.setViewer((Root) null);
} }
getRoot().componentDetached(this);
} }


/** /**
} }


public String getConnectorId() { 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;
} }
} }

+ 2
- 2
src/com/vaadin/ui/Component.java View File

import com.vaadin.terminal.Sizeable; import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.VariableOwner; import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.gwt.client.ComponentState; 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; import com.vaadin.terminal.gwt.server.RpcTarget;


/** /**
* @VERSION@ * @VERSION@
* @since 3.0 * @since 3.0
*/ */
public interface Component extends Connector, Paintable, VariableOwner,
public interface Component extends ClientConnector, Paintable, VariableOwner,
Sizeable, Serializable, RpcTarget { Sizeable, Serializable, RpcTarget {


/** /**

+ 102
- 0
src/com/vaadin/ui/DirtyConnectorTracker.java View File

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;
}

}

+ 16
- 0
src/com/vaadin/ui/Root.java View File

/** Identifies the click event */ /** Identifies the click event */
private static final String CLICK_EVENT_ID = VView.CLICK_EVENT_ID; 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 * Creates a new empty root without a caption. This root will have a
* {@link VerticalLayout} with margins enabled as its content. * {@link VerticalLayout} with margins enabled as its content.
// TODO How can a Root be invisible? What does it mean? // TODO How can a Root be invisible? What does it mean?
return isVisible() && isEnabled(); return isVisible() && isEnabled();
} }

public DirtyConnectorTracker getDirtyConnectorTracker() {
return dirtyConnectorTracker;
}

public void componentAttached(Component component) {
getDirtyConnectorTracker().componentAttached(component);
}

public void componentDetached(Component component) {
getDirtyConnectorTracker().componentDetached(component);
}

} }

Loading…
Cancel
Save