aboutsummaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/com/vaadin/server/ServletPortletHelper.java20
-rw-r--r--server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java203
-rw-r--r--server/src/com/vaadin/server/communication/PushAtmosphereHandler.java120
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java75
-rw-r--r--server/src/com/vaadin/server/communication/PushRequestHandler.java102
-rw-r--r--server/src/com/vaadin/ui/AbstractColorPicker.java111
-rw-r--r--server/src/com/vaadin/ui/AbstractComponent.java21
-rw-r--r--server/src/com/vaadin/ui/AbstractSelect.java26
-rw-r--r--server/src/com/vaadin/ui/AbstractSingleComponentContainer.java33
-rw-r--r--server/src/com/vaadin/ui/Component.java13
-rw-r--r--server/src/com/vaadin/ui/PopupView.java88
-rw-r--r--server/src/com/vaadin/ui/Table.java38
-rw-r--r--server/src/com/vaadin/ui/Window.java122
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignContext.java64
-rw-r--r--server/src/com/vaadin/ui/declarative/ShouldWriteDataDelegate.java55
-rw-r--r--server/src/com/vaadin/ui/declarative/converters/DesignShortcutActionConverter.java49
16 files changed, 992 insertions, 148 deletions
diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java
index 197d9fe416..33ecf16e17 100644
--- a/server/src/com/vaadin/server/ServletPortletHelper.java
+++ b/server/src/com/vaadin/server/ServletPortletHelper.java
@@ -102,6 +102,24 @@ public class ServletPortletHelper implements Serializable {
return false;
}
+ private static boolean isPathInfo(VaadinRequest request, String string) {
+ String pathInfo = request.getPathInfo();
+
+ if (pathInfo == null) {
+ return false;
+ }
+
+ if (!string.startsWith("/")) {
+ string = '/' + string;
+ }
+
+ if (pathInfo.equals(string)) {
+ return true;
+ }
+
+ return false;
+ }
+
public static boolean isFileUploadRequest(VaadinRequest request) {
return hasPathPrefix(request, UPLOAD_URL_PREFIX);
}
@@ -124,7 +142,7 @@ public class ServletPortletHelper implements Serializable {
}
public static boolean isPushRequest(VaadinRequest request) {
- return hasPathPrefix(request, ApplicationConstants.PUSH_PATH + '/');
+ return isPathInfo(request, ApplicationConstants.PUSH_PATH);
}
public static void initDefaultUIProvider(VaadinSession session,
diff --git a/server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java b/server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java
new file mode 100644
index 0000000000..fd61f6180f
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.server.communication;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRegistration;
+import javax.servlet.annotation.WebListener;
+
+import org.atmosphere.cpr.AtmosphereFramework;
+
+import com.vaadin.server.VaadinServlet;
+
+/**
+ * Initializer class for JSR 356 websockets.
+ * <p>
+ * Websocket specification says that initialization of websocket end points
+ * should be done in the servlet context initialization phase. Some servers
+ * implement this strictly so that end points cannot be registered after the
+ * context initialization phase.
+ * <p>
+ * Note that {@link WebListener} is Servlet 3.0 API so this will not be run for
+ * older servers (unless added to web.xml), but these servers do not support JSR
+ * 356 websockets either.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+@WebListener
+public class JSR356WebsocketInitializer implements ServletContextListener {
+
+ private static boolean atmosphereAvailable = false;
+ static {
+ try {
+ org.atmosphere.util.Version.getRawVersion();
+ atmosphereAvailable = true;
+ } catch (NoClassDefFoundError e) {
+ }
+ }
+
+ /**
+ * "ServletConfig" which only provides information from a
+ * {@link ServletRegistration} and its {@link ServletContext}
+ */
+ public static class FakeServletConfig implements ServletConfig {
+
+ private ServletRegistration servletRegistration;
+ private ServletContext servletContext;
+
+ public FakeServletConfig(ServletRegistration servletRegistration,
+ ServletContext servletContext) {
+ this.servletContext = servletContext;
+ this.servletRegistration = servletRegistration;
+ }
+
+ @Override
+ public String getServletName() {
+ return servletRegistration.getName();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return servletRegistration.getInitParameter(name);
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return Collections.enumeration(servletRegistration
+ .getInitParameters().keySet());
+ }
+
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext servletContext = sce.getServletContext();
+ if (servletContext.getMajorVersion() < 3) {
+ return;
+ }
+
+ if (!atmosphereAvailable) {
+ return;
+ }
+
+ Map<String, ? extends ServletRegistration> regs = servletContext
+ .getServletRegistrations();
+ for (String servletName : regs.keySet()) {
+ ServletRegistration servletRegistration = regs.get(servletName);
+
+ if (isVaadinServlet(servletRegistration)) {
+ try {
+ initAtmosphereForVaadinServlet(servletRegistration,
+ servletContext);
+ } catch (Exception e) {
+ getLogger().log(
+ Level.WARNING,
+ "Failed to initialize Atmosphere for "
+ + servletName, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Initializes Atmosphere for use with the given Vaadin servlet
+ * <p>
+ * For JSR 356 websockets to work properly, the initialization must be done
+ * in the servlet context initialization phase.
+ *
+ * @param servletRegistration
+ * The servlet registration info for the servlet
+ * @param servletContext
+ */
+ public static void initAtmosphereForVaadinServlet(
+ ServletRegistration servletRegistration,
+ ServletContext servletContext) {
+ String servletName = servletRegistration.getName();
+ String attributeName = getAttributeName(servletName);
+
+ if (servletContext.getAttribute(attributeName) != null) {
+ // Already initialized
+ getLogger().warning("Atmosphere already initialized");
+ return;
+ }
+ getLogger().finer("Creating AtmosphereFramework for " + servletName);
+ AtmosphereFramework framework = PushRequestHandler
+ .initAtmosphere(new FakeServletConfig(servletRegistration,
+ servletContext));
+ servletContext.setAttribute(attributeName, framework);
+ getLogger().finer("Created AtmosphereFramework for " + servletName);
+
+ }
+
+ /**
+ * Returns the name of the attribute in the servlet context where the
+ * pre-initialized Atmosphere object is stored
+ *
+ * @param servletName
+ * The name of the servlet
+ * @return The attribute name which contains the initialized Atmosphere
+ * object
+ */
+ public static String getAttributeName(String servletName) {
+ return JSR356WebsocketInitializer.class.getName() + "." + servletName;
+ }
+
+ /**
+ * Tries to determine if the given servlet registration refers to a Vaadin
+ * servlet.
+ *
+ * @param servletRegistration
+ * The servlet registration info for the servlet
+ * @return false if the servlet is definitely not a Vaadin servlet, true
+ * otherwise
+ */
+ protected boolean isVaadinServlet(ServletRegistration servletRegistration) {
+ try {
+ Class<?> servletClass = Class.forName(servletRegistration
+ .getClassName());
+ return VaadinServlet.class.isAssignableFrom(servletClass);
+ } catch (Exception e) {
+ // This will fail in OSGi environments, assume everything is a
+ // VaadinServlet
+ return true;
+ }
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(JSR356WebsocketInitializer.class.getName());
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ // Nothing to do here
+ }
+
+}
diff --git a/server/src/com/vaadin/server/communication/PushAtmosphereHandler.java b/server/src/com/vaadin/server/communication/PushAtmosphereHandler.java
new file mode 100644
index 0000000000..0e94eaa75f
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/PushAtmosphereHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.server.communication;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.atmosphere.cpr.AtmosphereRequest;
+import org.atmosphere.cpr.AtmosphereResource;
+import org.atmosphere.cpr.AtmosphereResourceEvent;
+import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
+import org.atmosphere.handler.AbstractReflectorAtmosphereHandler;
+
+/**
+ * Handles Atmosphere requests and forwards them to logical methods in
+ * {@link PushHandler}
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class PushAtmosphereHandler extends AbstractReflectorAtmosphereHandler
+ implements Serializable {
+
+ private PushHandler pushHandler = null;
+
+ public void setPushHandler(PushHandler pushHandler) {
+ this.pushHandler = pushHandler;
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(PushAtmosphereHandler.class.getName());
+ }
+
+ @Override
+ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
+ super.onStateChange(event);
+ if (pushHandler == null) {
+ getLogger()
+ .warning(
+ "AtmosphereHandler.onStateChange called before PushHandler has been set. This should really not happen");
+ return;
+ }
+
+ if (event.isCancelled() || event.isResumedOnTimeout()) {
+ pushHandler.connectionLost(event);
+ }
+ }
+
+ @Override
+ public void onRequest(AtmosphereResource resource) {
+ if (pushHandler == null) {
+ getLogger()
+ .warning(
+ "AtmosphereHandler.onRequest called before PushHandler has been set. This should really not happen");
+ return;
+ }
+
+ AtmosphereRequest req = resource.getRequest();
+
+ if (req.getMethod().equalsIgnoreCase("GET")) {
+ onConnect(resource);
+ } else if (req.getMethod().equalsIgnoreCase("POST")) {
+ onMessage(resource);
+ }
+ }
+
+ /**
+ * Called when the client sends a message through the push channel
+ *
+ * @param resource
+ */
+ private void onMessage(AtmosphereResource resource) {
+ pushHandler.onMessage(resource);
+ }
+
+ /**
+ * Called when the client sends the first request (to establish a push
+ * connection)
+ *
+ * @param resource
+ */
+ private void onConnect(AtmosphereResource resource) {
+ resource.addEventListener(new AtmosphereResourceListener());
+
+ pushHandler.onConnect(resource);
+ }
+
+ private class AtmosphereResourceListener extends
+ AtmosphereResourceEventListenerAdapter implements Serializable {
+
+ @Override
+ public void onDisconnect(AtmosphereResourceEvent event) {
+ // Log event on trace level
+ super.onDisconnect(event);
+ pushHandler.connectionLost(event);
+ }
+
+ @Override
+ public void onThrowable(AtmosphereResourceEvent event) {
+ getLogger().log(Level.SEVERE, "Exception in push connection",
+ event.throwable());
+ pushHandler.connectionLost(event);
+ }
+ }
+}
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java
index ea937d279e..552f2e9e03 100644
--- a/server/src/com/vaadin/server/communication/PushHandler.java
+++ b/server/src/com/vaadin/server/communication/PushHandler.java
@@ -22,14 +22,11 @@ import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.atmosphere.cpr.AtmosphereHandler;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResource.TRANSPORT;
import org.atmosphere.cpr.AtmosphereResourceEvent;
-import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.atmosphere.cpr.AtmosphereResourceImpl;
-import org.atmosphere.handler.AbstractReflectorAtmosphereHandler;
import com.vaadin.server.ErrorEvent;
import com.vaadin.server.ErrorHandler;
@@ -50,37 +47,13 @@ import com.vaadin.ui.UI;
import elemental.json.JsonException;
/**
- * Establishes bidirectional ("push") communication channels
+ * Handles incoming push connections and messages and dispatches them to the
+ * correct {@link UI}/ {@link AtmospherePushConnection}
*
* @author Vaadin Ltd
* @since 7.1
*/
-public class PushHandler extends AtmosphereResourceEventListenerAdapter {
-
- AtmosphereHandler handler = new AbstractReflectorAtmosphereHandler() {
-
- @Override
- public void onStateChange(AtmosphereResourceEvent event)
- throws IOException {
- super.onStateChange(event);
- if (event.isCancelled() || event.isResumedOnTimeout()) {
- connectionLost(event);
- }
- }
-
- @Override
- public void onRequest(AtmosphereResource resource) {
- AtmosphereRequest req = resource.getRequest();
-
- if (req.getMethod().equalsIgnoreCase("GET")) {
- callWithUi(resource, establishCallback, false);
- } else if (req.getMethod().equalsIgnoreCase("POST")) {
- callWithUi(resource, receiveCallback,
- resource.transport() == TRANSPORT.WEBSOCKET);
- }
- }
-
- };
+public class PushHandler {
/**
* Callback interface used internally to process an event with the
@@ -103,8 +76,6 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
"New push connection for resource {0} with transport {1}",
new Object[] { resource.uuid(), resource.transport() });
- resource.addEventListener(PushHandler.this);
-
resource.getResponse().setContentType("text/plain; charset=UTF-8");
VaadinSession session = ui.getSession();
@@ -325,21 +296,7 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
}
}
- @Override
- public void onDisconnect(AtmosphereResourceEvent event) {
- // Log event on trace level
- super.onDisconnect(event);
- connectionLost(event);
- }
-
- @Override
- public void onThrowable(AtmosphereResourceEvent event) {
- getLogger().log(Level.SEVERE, "Exception in push connection",
- event.throwable());
- connectionLost(event);
- }
-
- private void connectionLost(AtmosphereResourceEvent event) {
+ void connectionLost(AtmosphereResourceEvent event) {
// We don't want to use callWithUi here, as it assumes there's a client
// request active and does requestStart and requestEnd among other
// things.
@@ -506,4 +463,28 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
private static final Logger getLogger() {
return Logger.getLogger(PushHandler.class.getName());
}
+
+ /**
+ * Called when a new push connection is requested to be opened by the client
+ *
+ * @since
+ * @param resource
+ * The related atmosphere resources
+ */
+ void onConnect(AtmosphereResource resource) {
+ callWithUi(resource, establishCallback, false);
+ }
+
+ /**
+ * Called when a message is received through the push connection
+ *
+ * @since
+ * @param resource
+ * The related atmosphere resources
+ */
+ void onMessage(AtmosphereResource resource) {
+ callWithUi(resource, receiveCallback,
+ resource.transport() == TRANSPORT.WEBSOCKET);
+ }
+
}
diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java
index 74165a4988..0e3ec300b4 100644
--- a/server/src/com/vaadin/server/communication/PushRequestHandler.java
+++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java
@@ -17,6 +17,8 @@
package com.vaadin.server.communication;
import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -25,6 +27,8 @@ import org.atmosphere.cache.UUIDBroadcasterCache;
import org.atmosphere.client.TrackMessageSizeInterceptor;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereFramework;
+import org.atmosphere.cpr.AtmosphereFramework.AtmosphereHandlerWrapper;
+import org.atmosphere.cpr.AtmosphereHandler;
import org.atmosphere.cpr.AtmosphereInterceptor;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResponse;
@@ -48,7 +52,8 @@ import com.vaadin.shared.communication.PushConstants;
/**
* Handles requests to open a push (bidirectional) communication channel between
* the client and the server. After the initial request, communication through
- * the push channel is managed by {@link PushHandler}.
+ * the push channel is managed by {@link PushAtmosphereHandler} and
+ * {@link PushHandler}
*
* @author Vaadin Ltd
* @since 7.1
@@ -62,10 +67,85 @@ public class PushRequestHandler implements RequestHandler,
public PushRequestHandler(VaadinServletService service)
throws ServiceException {
+ service.addServiceDestroyListener(new ServiceDestroyListener() {
+ @Override
+ public void serviceDestroy(ServiceDestroyEvent event) {
+ destroy();
+ }
+ });
+
final ServletConfig vaadinServletConfig = service.getServlet()
.getServletConfig();
- atmosphere = new AtmosphereFramework() {
+ pushHandler = new PushHandler(service);
+
+ atmosphere = getPreInitializedAtmosphere(vaadinServletConfig);
+ if (atmosphere == null) {
+ // Not initialized by JSR356WebsocketInitializer
+ getLogger().fine(
+ "Initializing Atmosphere for servlet "
+ + vaadinServletConfig.getServletName());
+ try {
+ atmosphere = initAtmosphere(vaadinServletConfig);
+ } catch (Exception e) {
+ getLogger().log(
+ Level.WARNING,
+ "Failed to initialize Atmosphere for "
+ + service.getServlet().getServletName()
+ + ". Push will not work.", e);
+ return;
+ }
+ } else {
+ getLogger().fine(
+ "Using pre-initialized Atmosphere for servlet "
+ + vaadinServletConfig.getServletName());
+ }
+
+ for (AtmosphereHandlerWrapper handlerWrapper : atmosphere
+ .getAtmosphereHandlers().values()) {
+ AtmosphereHandler handler = handlerWrapper.atmosphereHandler;
+ if (handler instanceof PushAtmosphereHandler) {
+ // Map the (possibly pre-initialized) handler to the actual push
+ // handler
+ ((PushAtmosphereHandler) handler).setPushHandler(pushHandler);
+ }
+
+ }
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(PushRequestHandler.class.getName());
+ }
+
+ /**
+ * Returns an AtmosphereFramework instance which was initialized in the
+ * servlet context init phase by {@link JSR356WebsocketInitializer}, if such
+ * exists
+ */
+ private AtmosphereFramework getPreInitializedAtmosphere(
+ ServletConfig vaadinServletConfig) {
+ String attributeName = JSR356WebsocketInitializer
+ .getAttributeName(vaadinServletConfig.getServletName());
+ Object framework = vaadinServletConfig.getServletContext()
+ .getAttribute(attributeName);
+ if (framework != null && framework instanceof AtmosphereFramework) {
+ return (AtmosphereFramework) framework;
+ }
+
+ return null;
+ }
+
+ /**
+ * Initializes Atmosphere for the given ServletConfiguration
+ *
+ * @since
+ * @param vaadinServletConfig
+ * The servlet configuration for the servlet which should have
+ * Atmosphere support
+ */
+ static AtmosphereFramework initAtmosphere(
+ final ServletConfig vaadinServletConfig) {
+ AtmosphereFramework atmosphere = new AtmosphereFramework() {
@Override
protected void analytics() {
// Overridden to disable version number check
@@ -81,15 +161,7 @@ public class PushRequestHandler implements RequestHandler,
}
};
- service.addServiceDestroyListener(new ServiceDestroyListener() {
- @Override
- public void serviceDestroy(ServiceDestroyEvent event) {
- destroy();
- }
- });
-
- pushHandler = new PushHandler(service);
- atmosphere.addAtmosphereHandler("/*", pushHandler.handler);
+ atmosphere.addAtmosphereHandler("/*", new PushAtmosphereHandler());
atmosphere.addInitParameter(ApplicationConfig.BROADCASTER_CACHE,
UUIDBroadcasterCache.class.getName());
atmosphere.addInitParameter(ApplicationConfig.ANNOTATION_PROCESSOR,
@@ -131,8 +203,9 @@ public class PushRequestHandler implements RequestHandler,
trackMessageSize.configure(atmosphere.getAtmosphereConfig());
atmosphere.interceptor(trackMessageSize);
} catch (ServletException e) {
- throw new ServiceException("Atmosphere init failed", e);
+ throw new RuntimeException("Atmosphere init failed", e);
}
+ return atmosphere;
}
@Override
@@ -144,6 +217,11 @@ public class PushRequestHandler implements RequestHandler,
}
if (request instanceof VaadinServletRequest) {
+ if (atmosphere == null) {
+ response.sendError(500,
+ "Atmosphere initialization failed. No push available.");
+ return true;
+ }
try {
atmosphere.doCometSupport(AtmosphereRequest
.wrap((VaadinServletRequest) request),
diff --git a/server/src/com/vaadin/ui/AbstractColorPicker.java b/server/src/com/vaadin/ui/AbstractColorPicker.java
index 608a42d33b..3212d1b23f 100644
--- a/server/src/com/vaadin/ui/AbstractColorPicker.java
+++ b/server/src/com/vaadin/ui/AbstractColorPicker.java
@@ -17,6 +17,10 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.Collection;
+
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
import com.vaadin.shared.ui.colorpicker.Color;
import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc;
@@ -27,6 +31,8 @@ import com.vaadin.ui.components.colorpicker.ColorChangeEvent;
import com.vaadin.ui.components.colorpicker.ColorChangeListener;
import com.vaadin.ui.components.colorpicker.ColorPickerPopup;
import com.vaadin.ui.components.colorpicker.ColorSelector;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
/**
* An abstract class that defines default implementation for a color picker
@@ -276,6 +282,16 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
}
/**
+ * Gets the style for the popup window
+ *
+ * @since
+ * @return popup window style
+ */
+ public PopupStyle getPopupStyle() {
+ return popupStyle;
+ }
+
+ /**
* Set the visibility of the RGB Tab
*
* @param visible
@@ -294,6 +310,16 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
}
/**
+ * Gets the visibility of the RGB Tab
+ *
+ * @since
+ * @return visibility of the RGB tab
+ */
+ public boolean getRGBVisibility() {
+ return rgbVisible;
+ }
+
+ /**
* Set the visibility of the HSV Tab
*
* @param visible
@@ -311,6 +337,16 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
}
/**
+ * Gets the visibility of the HSV Tab
+ *
+ * @since
+ * @return visibility of the HSV tab
+ */
+ public boolean getHSVVisibility() {
+ return hsvVisible;
+ }
+
+ /**
* Set the visibility of the Swatches Tab
*
* @param visible
@@ -328,6 +364,16 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
}
/**
+ * Gets the visibility of the Swatches Tab
+ *
+ * @since
+ * @return visibility of the swatches tab
+ */
+ public boolean getSwatchesVisibility() {
+ return swatchesVisible;
+ }
+
+ /**
* Sets the visibility of the Color History
*
* @param visible
@@ -341,6 +387,16 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
}
/**
+ * Gets the visibility of the Color History
+ *
+ * @since
+ * @return visibility of color history
+ */
+ public boolean getHistoryVisibility() {
+ return historyVisible;
+ }
+
+ /**
* Sets the visibility of the CSS color code text field
*
* @param visible
@@ -353,6 +409,16 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
}
}
+ /**
+ * Gets the visibility of CSS color code text field
+ *
+ * @since
+ * @return visibility of css color code text field
+ */
+ public boolean getTextfieldVisibility() {
+ return textfieldVisible;
+ }
+
@Override
protected ColorPickerState getState() {
return (ColorPickerState) super.getState();
@@ -473,4 +539,49 @@ public abstract class AbstractColorPicker extends AbstractComponent implements
public boolean isHtmlContentAllowed() {
return isCaptionAsHtml();
}
+
+ @Override
+ public void readDesign(Element design, DesignContext designContext) {
+ super.readDesign(design, designContext);
+
+ Attributes attributes = design.attributes();
+ if (design.hasAttr("color")) {
+ // Ignore the # character
+ String hexColor = DesignAttributeHandler.readAttribute("color",
+ attributes, String.class).substring(1);
+ setColor(new Color(Integer.parseInt(hexColor, 16)));
+ }
+ if (design.hasAttr("popup-style")) {
+ setPopupStyle(PopupStyle.valueOf("POPUP_"
+ + attributes.get("popup-style").toUpperCase()));
+ }
+ if (design.hasAttr("position")) {
+ String[] position = attributes.get("position").split(",");
+ setPosition(Integer.parseInt(position[0]),
+ Integer.parseInt(position[1]));
+ }
+ }
+
+ @Override
+ public void writeDesign(Element design, DesignContext designContext) {
+ super.writeDesign(design, designContext);
+
+ Attributes attribute = design.attributes();
+ DesignAttributeHandler.writeAttribute("color", attribute,
+ color.getCSS(), Color.WHITE.getCSS(), String.class);
+ DesignAttributeHandler.writeAttribute("popup-style", attribute,
+ (popupStyle == PopupStyle.POPUP_NORMAL ? "normal" : "simple"),
+ "normal", String.class);
+ DesignAttributeHandler.writeAttribute("position", attribute, positionX
+ + "," + positionY, "0,0", String.class);
+ }
+
+ @Override
+ protected Collection<String> getCustomAttributes() {
+ Collection<String> result = super.getCustomAttributes();
+ result.add("color");
+ result.add("position");
+ result.add("popup-style");
+ return result;
+ }
}
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java
index ebe438b908..dae073904b 100644
--- a/server/src/com/vaadin/ui/AbstractComponent.java
+++ b/server/src/com/vaadin/ui/AbstractComponent.java
@@ -982,11 +982,6 @@ public abstract class AbstractComponent extends AbstractClientConnector
Integer.class));
}
- // handle responsive
- if (attr.hasKey("responsive")) {
- setResponsive(DesignAttributeHandler.getFormatter().parse(
- attr.get("responsive"), Boolean.class));
- }
// check for unsupported attributes
Set<String> supported = new HashSet<String>();
supported.addAll(getDefaultAttributes());
@@ -1032,11 +1027,11 @@ public abstract class AbstractComponent extends AbstractClientConnector
/**
* Toggles responsiveness of this component.
*
- * @since 7.4
+ * @since
* @param responsive
* boolean enables responsiveness, false disables
*/
- private void setResponsive(boolean responsive) {
+ public void setResponsive(boolean responsive) {
if (responsive) {
// make responsive if necessary
if (!isResponsive()) {
@@ -1057,10 +1052,10 @@ public abstract class AbstractComponent extends AbstractClientConnector
/**
* Returns true if the component is responsive
*
- * @since 7.4
+ * @since
* @return true if the component is responsive
*/
- private boolean isResponsive() {
+ public boolean isResponsive() {
for (Extension e : getExtensions()) {
if (e instanceof Responsive) {
return true;
@@ -1233,8 +1228,8 @@ public abstract class AbstractComponent extends AbstractClientConnector
private static final String[] customAttributes = new String[] { "width",
"height", "debug-id", "error", "width-auto", "height-auto",
- "width-full", "height-full", "size-auto", "size-full",
- "responsive", "immediate", "locale", "read-only", "_id" };
+ "width-full", "height-full", "size-auto", "size-full", "immediate",
+ "locale", "read-only", "_id" };
/*
* (non-Javadoc)
@@ -1280,10 +1275,6 @@ public abstract class AbstractComponent extends AbstractClientConnector
((Focusable) def).getTabIndex(), Integer.class);
}
- // handle responsive
- if (isResponsive()) {
- attr.put("responsive", "");
- }
}
/*
diff --git a/server/src/com/vaadin/ui/AbstractSelect.java b/server/src/com/vaadin/ui/AbstractSelect.java
index 06790ca78d..4c5e6b6ea3 100644
--- a/server/src/com/vaadin/ui/AbstractSelect.java
+++ b/server/src/com/vaadin/ui/AbstractSelect.java
@@ -2222,4 +2222,30 @@ public abstract class AbstractSelect extends AbstractField<Object> implements
}
}
}
+
+ @Override
+ public void writeDesign(Element design, DesignContext designContext) {
+ // Write default attributes
+ super.writeDesign(design, designContext);
+
+ // Write options if warranted
+ if (designContext.shouldWriteData(this)) {
+ for (Object itemId : getItemIds()) {
+ Element optionElement = design.appendElement("option");
+
+ optionElement.html(getItemCaption(itemId));
+
+ Resource icon = getItemIcon(itemId);
+ if (icon != null) {
+ DesignAttributeHandler.writeAttribute("icon",
+ optionElement.attributes(), icon, null,
+ Resource.class);
+ }
+
+ if (isSelected(itemId)) {
+ optionElement.attr("selected", "");
+ }
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java b/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java
index 244feb3bb9..767ae66515 100644
--- a/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java
+++ b/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java
@@ -19,6 +19,7 @@ import java.util.Collections;
import java.util.Iterator;
import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.server.VaadinService;
@@ -288,17 +289,33 @@ public abstract class AbstractSingleComponentContainer extends
public void readDesign(Element design, DesignContext designContext) {
// process default attributes
super.readDesign(design, designContext);
- // handle child element, checking that the design specifies at most one
- // child
- int childCount = design.children().size();
- if (childCount > 1) {
+ readDesignChildren(design.children(), designContext);
+ }
+
+ /**
+ * Reads the content component from the list of child elements of a design.
+ * The list must be empty or contain a single element; if the design
+ * contains multiple child elements, a DesignException is thrown. This
+ * method should be overridden by subclasses whose design may contain
+ * non-content child elements.
+ *
+ * @param children
+ * the child elements of the design that is being read
+ * @param context
+ * the DesignContext instance used to parse the design
+ *
+ * @throws DesignException
+ * if there are multiple child elements
+ * @throws DesignException
+ * if a child element could not be parsed as a Component
+ */
+ protected void readDesignChildren(Elements children, DesignContext context) {
+ if (children.size() > 1) {
throw new DesignException("The container of type "
+ getClass().toString()
+ " can have only one child component.");
- } else if (childCount == 1) {
- Element childElement = design.children().get(0);
- Component newChild = designContext.readDesign(childElement);
- setContent(newChild);
+ } else if (children.size() == 1) {
+ setContent(context.readDesign(children.first()));
}
}
diff --git a/server/src/com/vaadin/ui/Component.java b/server/src/com/vaadin/ui/Component.java
index 9e0816a398..adef4b69c5 100644
--- a/server/src/com/vaadin/ui/Component.java
+++ b/server/src/com/vaadin/ui/Component.java
@@ -149,8 +149,8 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
*
* <p>
* Each style name will occur in two versions: one as specified and one that
- * is prefixed wil the style name of the component. For example, if you have
- * a {@code Button} component and give it "{@code mystyle}" style, the
+ * is prefixed with the style name of the component. For example, if you
+ * have a {@code Button} component and give it "{@code mystyle}" style, the
* component will have both "{@code mystyle}" and "{@code v-button-mystyle}"
* styles. You could then style the component either with:
* </p>
@@ -253,9 +253,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
public boolean isEnabled();
/**
- * Enables or disables the component. The user can not interact disabled
- * components, which are shown with a style that indicates the status,
- * usually shaded in light gray color. Components are enabled by default.
+ * Enables or disables the component. The user can not interact with
+ * disabled components, which are shown with a style that indicates the
+ * status, usually shaded in light gray color. Components are enabled by
+ * default.
*
* <pre>
* Button enabled = new Button(&quot;Enabled&quot;);
@@ -909,7 +910,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* </pre>
*
* @param event
- * the event that has occured.
+ * the event that has occurred.
*/
public void componentEvent(Component.Event event);
}
diff --git a/server/src/com/vaadin/ui/PopupView.java b/server/src/com/vaadin/ui/PopupView.java
index 2a2da26b62..12034cb56c 100644
--- a/server/src/com/vaadin/ui/PopupView.java
+++ b/server/src/com/vaadin/ui/PopupView.java
@@ -20,8 +20,13 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
+import org.jsoup.parser.Tag;
+
import com.vaadin.shared.ui.popupview.PopupViewServerRpc;
import com.vaadin.shared.ui.popupview.PopupViewState;
+import com.vaadin.ui.declarative.DesignContext;
/**
*
@@ -61,9 +66,15 @@ public class PopupView extends AbstractComponent implements HasComponents {
/* Constructors */
- private PopupView() {
+ /**
+ * This is an internal constructor. Use
+ * {@link PopupView#PopupView(String, Component)} instead.
+ */
+ @Deprecated
+ public PopupView() {
registerRpc(rpc);
setHideOnMouseOut(true);
+ setContent(createContent("", new Label("")));
}
/**
@@ -77,18 +88,7 @@ public class PopupView extends AbstractComponent implements HasComponents {
* the full, Component-type representation
*/
public PopupView(final java.lang.String small, final Component large) {
- this(new PopupView.Content() {
- @Override
- public java.lang.String getMinimizedValueAsHTML() {
- return small;
- }
-
- @Override
- public Component getPopupComponent() {
- return large;
- }
- });
-
+ this(createContent(small, large));
}
/**
@@ -104,6 +104,30 @@ public class PopupView extends AbstractComponent implements HasComponents {
}
/**
+ * Creates a Content from given text representation and popup content.
+ *
+ * @param minimizedValue
+ * text representation when popup is hidden
+ * @param popupContent
+ * popup content
+ * @return content with given data
+ */
+ protected static Content createContent(final String minimizedValue,
+ final Component popupContent) {
+ return new Content() {
+ @Override
+ public String getMinimizedValueAsHTML() {
+ return minimizedValue;
+ }
+
+ @Override
+ public Component getPopupComponent() {
+ return popupContent;
+ }
+ };
+ }
+
+ /**
* This method will replace the current content of the panel with a new one.
*
* @param newContent
@@ -233,6 +257,44 @@ public class PopupView extends AbstractComponent implements HasComponents {
}
@Override
+ public void readDesign(Element design, DesignContext designContext) {
+
+ // Read content first to avoid NPE when setting popup visible
+ Component popupContent = null;
+ String minimizedValue = "";
+ for (Node childNode : design.childNodes()) {
+ if (childNode instanceof Element) {
+ Element child = (Element) childNode;
+ if (child.tagName().equals("popup-content")) {
+ popupContent = designContext.readDesign(child.child(0));
+ } else {
+ minimizedValue += child.toString();
+ }
+ } else {
+ minimizedValue += childNode.toString();
+ }
+ }
+ setContent(createContent(minimizedValue.trim(), popupContent));
+
+ super.readDesign(design, designContext);
+ }
+
+ @Override
+ public void writeDesign(Element design, DesignContext designContext) {
+ super.writeDesign(design, designContext);
+
+ Element popupContent = new Element(Tag.valueOf("popup-content"), "");
+ popupContent.appendChild(designContext.createElement(content
+ .getPopupComponent()));
+
+ String minimizedHTML = content.getMinimizedValueAsHTML();
+ if (minimizedHTML != null && !minimizedHTML.isEmpty()) {
+ design.append(minimizedHTML);
+ }
+ design.appendChild(popupContent);
+ }
+
+ @Override
protected PopupViewState getState() {
return (PopupViewState) super.getState();
}
diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java
index 316564c7e3..347bb8fbff 100644
--- a/server/src/com/vaadin/ui/Table.java
+++ b/server/src/com/vaadin/ui/Table.java
@@ -700,7 +700,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Gets the headers of the columns.
*
* <p>
- * The headers match the property id:s given my the set visible column
+ * The headers match the property id:s given by the set visible column
* headers. The table must be set in either
* {@link #COLUMN_HEADER_MODE_EXPLICIT} or
* {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the
@@ -727,7 +727,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Sets the headers of the columns.
*
* <p>
- * The headers match the property id:s given my the set visible column
+ * The headers match the property id:s given by the set visible column
* headers. The table must be set in either
* {@link #COLUMN_HEADER_MODE_EXPLICIT} or
* {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the
@@ -760,7 +760,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Gets the icons of the columns.
*
* <p>
- * The icons in headers match the property id:s given my the set visible
+ * The icons in headers match the property id:s given by the set visible
* column headers. The table must be set in either
* {@link #COLUMN_HEADER_MODE_EXPLICIT} or
* {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers
@@ -787,7 +787,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Sets the icons of the columns.
*
* <p>
- * The icons in headers match the property id:s given my the set visible
+ * The icons in headers match the property id:s given by the set visible
* column headers. The table must be set in either
* {@link #COLUMN_HEADER_MODE_EXPLICIT} or
* {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers
@@ -887,7 +887,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
/**
- * Sets columns width (in pixels). Theme may not necessary respect very
+ * Sets columns width (in pixels). Theme may not necessarily respect very
* small or very big values. Setting width to -1 (default) means that theme
* will make decision of width.
*
@@ -896,9 +896,9 @@ public class Table extends AbstractSelect implements Action.Container,
* is used. See @link {@link #setColumnExpandRatio(Object, float)}.
*
* @param propertyId
- * colunmns property id
+ * columns property id
* @param width
- * width to be reserved for colunmns content
+ * width to be reserved for columns content
* @since 4.0.3
*/
public void setColumnWidth(Object propertyId, int width) {
@@ -975,7 +975,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
/**
- * Gets the column expand ratio for a columnd. See
+ * Gets the column expand ratio for a column. See
* {@link #setColumnExpandRatio(Object, float)}
*
* @param propertyId
@@ -1091,7 +1091,7 @@ public class Table extends AbstractSelect implements Action.Container,
*/
public Object getCurrentPageFirstItemId() {
- // Priorise index over id if indexes are supported
+ // Prioritise index over id if indexes are supported
if (items instanceof Container.Indexed) {
final int index = getCurrentPageFirstItemIndex();
Object id = null;
@@ -1193,7 +1193,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Gets the icon Resource for the specified column.
*
* @param propertyId
- * the propertyId indentifying the column.
+ * the propertyId identifying the column.
* @return the icon for the specified column; null if the column has no icon
* set, or if the column is not visible.
*/
@@ -2598,7 +2598,7 @@ public class Table extends AbstractSelect implements Action.Container,
* types.
* @param itemId
* the Id the new row. If null, a new id is automatically
- * assigned. If given, the table cant already have a item with
+ * assigned. If given, the table cannot already have a item with
* given id.
* @return Returns item id for the new row. Returns null if operation fails.
*/
@@ -4326,7 +4326,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Adds a new property to the table and show it as a visible column.
*
* @param propertyId
- * the Id of the proprty.
+ * the Id of the property.
* @param type
* the class of the property.
* @param defaultValue
@@ -4361,7 +4361,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Adds a new property to the table and show it as a visible column.
*
* @param propertyId
- * the Id of the proprty
+ * the Id of the property
* @param type
* the class of the property
* @param defaultValue
@@ -4571,7 +4571,7 @@ public class Table extends AbstractSelect implements Action.Container,
disableContentRefreshing();
super.containerPropertySetChange(event);
- // sanitetize visibleColumns. note that we are not adding previously
+ // sanitize visibleColumns. note that we are not adding previously
// non-existing properties as columns
Collection<?> containerPropertyIds = getContainerDataSource()
.getContainerPropertyIds();
@@ -4757,11 +4757,11 @@ public class Table extends AbstractSelect implements Action.Container,
* If table is editable a editor of type Field is created for each table
* cell. The assigned FieldFactory is used to create the instances.
*
- * To provide custom editors for table cells create a class implementins the
+ * To provide custom editors for table cells create a class implementing the
* FieldFactory interface, and assign it to table, and set the editable
* property to true.
*
- * @return true if table is editable, false oterwise.
+ * @return true if table is editable, false otherwise.
* @see Field
* @see FieldFactory
*
@@ -4776,7 +4776,7 @@ public class Table extends AbstractSelect implements Action.Container,
* If table is editable a editor of type Field is created for each table
* cell. The assigned FieldFactory is used to create the instances.
*
- * To provide custom editors for table cells create a class implementins the
+ * To provide custom editors for table cells create a class implementing the
* FieldFactory interface, and assign it to table, and set the editable
* property to true.
*
@@ -5346,7 +5346,7 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Gets the property id of the column which header was pressed
*
- * @return The column propety id
+ * @return The column property id
*/
public Object getPropertyId() {
return columnPropertyId;
@@ -5396,7 +5396,7 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Gets the property id of the column which header was pressed
*
- * @return The column propety id
+ * @return The column property id
*/
public Object getPropertyId() {
return columnPropertyId;
diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java
index 653b620746..e7764ffd8d 100644
--- a/server/src/com/vaadin/ui/Window.java
+++ b/server/src/com/vaadin/ui/Window.java
@@ -18,11 +18,18 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
@@ -42,6 +49,9 @@ import com.vaadin.shared.ui.window.WindowMode;
import com.vaadin.shared.ui.window.WindowRole;
import com.vaadin.shared.ui.window.WindowServerRpc;
import com.vaadin.shared.ui.window.WindowState;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignException;
import com.vaadin.util.ReflectTools;
/**
@@ -1283,4 +1293,116 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
public String getTabStopBottomAssistiveText() {
return getState(false).assistiveTabStopBottomText;
}
+
+ @Override
+ public void readDesign(Element design, DesignContext context) {
+ super.readDesign(design, context);
+
+ if (design.hasAttr("center")) {
+ center();
+ }
+ if (design.hasAttr("position")) {
+ String[] position = design.attr("position").split(",");
+ setPositionX(Integer.parseInt(position[0]));
+ setPositionY(Integer.parseInt(position[1]));
+ }
+ if (design.hasAttr("close-shortcut")) {
+ ShortcutAction shortcut = DesignAttributeHandler
+ .readAttribute("close-shortcut", design.attributes(),
+ ShortcutAction.class);
+ setCloseShortcut(shortcut.getKeyCode(), shortcut.getModifiers());
+ }
+ }
+
+ /**
+ * Reads the content and possible assistive descriptions from the list of
+ * child elements of a design. If an element has an
+ * {@code :assistive-description} attribute, adds the parsed component to
+ * the list of components used as the assistive description of this Window.
+ * Otherwise, sets the component as the content of this Window. If there are
+ * multiple non-description elements, throws a DesignException.
+ *
+ * @param children
+ * child elements in a design
+ * @param context
+ * the DesignContext instance used to parse the design
+ *
+ * @throws DesignException
+ * if there are multiple non-description child elements
+ * @throws DesignException
+ * if a child element could not be parsed as a Component
+ *
+ * @see #setContent(Component)
+ * @see #setAssistiveDescription(Component...)
+ */
+ @Override
+ protected void readDesignChildren(Elements children, DesignContext context) {
+ List<Component> descriptions = new ArrayList<Component>();
+ Elements content = new Elements();
+
+ for (Element child : children) {
+ if (child.hasAttr(":assistive-description")) {
+ descriptions.add(context.readDesign(child));
+ } else {
+ content.add(child);
+ }
+ }
+ super.readDesignChildren(content, context);
+ setAssistiveDescription(descriptions.toArray(new Component[0]));
+ }
+
+ @Override
+ public void writeDesign(Element design, DesignContext context) {
+ super.writeDesign(design, context);
+
+ Window def = context.getDefaultInstance(this);
+
+ if (getState().centered) {
+ design.attr("center", "");
+ }
+
+ DesignAttributeHandler.writeAttribute("position", design.attributes(),
+ getPosition(), def.getPosition(), String.class);
+
+ CloseShortcut shortcut = getCloseShortcut();
+ if (shortcut != null) {
+ // TODO What if several close shortcuts??
+
+ CloseShortcut defShortcut = def.getCloseShortcut();
+ if (defShortcut == null
+ || shortcut.getKeyCode() != defShortcut.getKeyCode()
+ || !Arrays.equals(shortcut.getModifiers(),
+ defShortcut.getModifiers())) {
+ DesignAttributeHandler.writeAttribute("close-shortcut",
+ design.attributes(), shortcut, null,
+ CloseShortcut.class);
+ }
+ }
+
+ for (Component c : getAssistiveDescription()) {
+ Element child = context.createElement(c).attr(
+ ":assistive-description", "");
+ design.appendChild(child);
+ }
+ }
+
+ private String getPosition() {
+ return getPositionX() + "," + getPositionY();
+ }
+
+ private CloseShortcut getCloseShortcut() {
+ Iterator<CloseShortcut> i = getCloseShortcuts().iterator();
+ return i.hasNext() ? i.next() : null;
+ }
+
+ @Override
+ protected Collection<String> getCustomAttributes() {
+ Collection<String> result = super.getCustomAttributes();
+ result.add("center");
+ result.add("position");
+ result.add("position-y");
+ result.add("position-x");
+ result.add("close-shortcut");
+ return result;
+ }
}
diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java
index f991b3013a..fe3abcfb77 100644
--- a/server/src/com/vaadin/ui/declarative/DesignContext.java
+++ b/server/src/com/vaadin/ui/declarative/DesignContext.java
@@ -75,6 +75,8 @@ public class DesignContext implements Serializable {
// component creation listeners
private List<ComponentCreationListener> listeners = new ArrayList<ComponentCreationListener>();
+ private ShouldWriteDataDelegate shouldWriteDataDelegate = ShouldWriteDataDelegate.DEFAULT;
+
public DesignContext(Document doc) {
this.doc = doc;
// Initialize the mapping between prefixes and package names.
@@ -168,16 +170,16 @@ public class DesignContext implements Serializable {
* component, the mapping from c to the string is removed. Similarly, if
* component was mapped to some string s different from localId, the mapping
* from s to component is removed.
- *
- * @param localId
- * The new local id of the component.
* @param component
* The component whose local id is to be set.
+ * @param localId
+ * The new local id of the component.
+ *
* @return true, if there already was a local id mapping from the string to
* some component or from the component to some string. Otherwise
* returns false.
*/
- private boolean mapLocalId(String localId, Component component) {
+ public boolean setComponentLocalId(Component component, String localId) {
return twoWayMap(localId, component, localIdToComponent,
componentToLocalId);
}
@@ -464,7 +466,7 @@ public class DesignContext implements Serializable {
// from the attributes of componentDesign
if (attributes.hasKey(LOCAL_ID_ATTRIBUTE)) {
String localId = attributes.get(LOCAL_ID_ATTRIBUTE);
- boolean mappingExists = mapLocalId(localId, component);
+ boolean mappingExists = setComponentLocalId(component, localId);
if (mappingExists) {
throw new DesignException(
"the following local id is not unique: " + localId);
@@ -661,4 +663,56 @@ public class DesignContext implements Serializable {
return true;
}
+
+ /**
+ * Determines whether the container data of a component should be written
+ * out by delegating to a {@link ShouldWriteDataDelegate}. The default
+ * delegate assumes that all component data is provided by a data source
+ * connected to a back end system and that the data should thus not be
+ * written.
+ *
+ * @since
+ * @see #setShouldWriteDataDelegate(ShouldWriteDataDelegate)
+ * @param component
+ * the component to check
+ * @return <code>true</code> if container data should be written out for the
+ * provided component; otherwise <code>false</code>.
+ */
+ public boolean shouldWriteData(Component component) {
+ return getShouldWriteDataDelegate().shouldWriteData(component);
+ }
+
+ /**
+ * Sets the delegate that determines whether the container data of a
+ * component should be written out.
+ *
+ * @since
+ * @see #shouldWriteChildren(Component, Component)
+ * @see #getShouldWriteDataDelegate()
+ * @param shouldWriteDataDelegate
+ * the delegate to set, not <code>null</code>
+ * @throws IllegalArgumentException
+ * if the provided delegate is <code>null</code>
+ */
+ public void setShouldWriteDataDelegate(
+ ShouldWriteDataDelegate shouldWriteDataDelegate) {
+ if (shouldWriteDataDelegate == null) {
+ throw new IllegalArgumentException("Delegate cannot be null");
+ }
+ this.shouldWriteDataDelegate = shouldWriteDataDelegate;
+ }
+
+ /**
+ * Gets the delegate that determines whether the container data of a
+ * component should be written out.
+ *
+ * @since
+ * @see #setShouldWriteDataDelegate(ShouldWriteDataDelegate)
+ * @see #shouldWriteChildren(Component, Component)
+ * @return the shouldWriteDataDelegate the currently use delegate
+ */
+ public ShouldWriteDataDelegate getShouldWriteDataDelegate() {
+ return shouldWriteDataDelegate;
+ }
+
}
diff --git a/server/src/com/vaadin/ui/declarative/ShouldWriteDataDelegate.java b/server/src/com/vaadin/ui/declarative/ShouldWriteDataDelegate.java
new file mode 100644
index 0000000000..29a78e1db6
--- /dev/null
+++ b/server/src/com/vaadin/ui/declarative/ShouldWriteDataDelegate.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.declarative;
+
+import java.io.Serializable;
+
+import com.vaadin.ui.Component;
+
+/**
+ * Delegate used by {@link DesignContext} to determine whether container data
+ * should be written out for a component.
+ *
+ * @see DesignContext#shouldWriteData(Component)
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public interface ShouldWriteDataDelegate extends Serializable {
+
+ /**
+ * The default delegate implementation that assumes that all component data
+ * is provided by a data source connected to a back end system and that the
+ * data should thus not be written.
+ */
+ public static final ShouldWriteDataDelegate DEFAULT = new ShouldWriteDataDelegate() {
+ @Override
+ public boolean shouldWriteData(Component component) {
+ return false;
+ }
+ };
+
+ /**
+ * Determines whether the container data of a component should be written
+ * out.
+ *
+ * @param component
+ * the component to check
+ * @return <code>true</code> if container data should be written out for the
+ * provided component; otherwise <code>false</code>.
+ */
+ boolean shouldWriteData(Component component);
+}
diff --git a/server/src/com/vaadin/ui/declarative/converters/DesignShortcutActionConverter.java b/server/src/com/vaadin/ui/declarative/converters/DesignShortcutActionConverter.java
index e2b6ed8e14..d6f2f65938 100644
--- a/server/src/com/vaadin/ui/declarative/converters/DesignShortcutActionConverter.java
+++ b/server/src/com/vaadin/ui/declarative/converters/DesignShortcutActionConverter.java
@@ -126,32 +126,37 @@ public class DesignShortcutActionConverter implements
if (value.length() == 0) {
return null;
}
- String[] data = value.split(" ", 2);
+ String[] data = value.split(" ", 2);
String[] parts = data[0].split("-");
- // handle keycode
- String keyCodePart = parts[parts.length - 1];
- int keyCode = getKeycodeForString(keyCodePart);
- if (keyCode < 0) {
- throw new IllegalArgumentException("Invalid shortcut definition "
- + value);
- }
- // handle modifiers
- int[] modifiers = null;
- if (parts.length > 1) {
- modifiers = new int[parts.length - 1];
- }
- for (int i = 0; i < parts.length - 1; i++) {
- int modifier = getKeycodeForString(parts[i]);
- if (modifier > 0) {
- modifiers[i] = modifier;
- } else {
- throw new IllegalArgumentException(
- "Invalid shortcut definition " + value);
+
+ try {
+ // handle keycode
+ String keyCodePart = parts[parts.length - 1];
+ int keyCode = getKeycodeForString(keyCodePart);
+ if (keyCode < 0) {
+ throw new IllegalArgumentException("Invalid key '"
+ + keyCodePart + "'");
+ }
+ // handle modifiers
+ int[] modifiers = null;
+ if (parts.length > 1) {
+ modifiers = new int[parts.length - 1];
+ }
+ for (int i = 0; i < parts.length - 1; i++) {
+ int modifier = getKeycodeForString(parts[i]);
+ if (modifier > 0) {
+ modifiers[i] = modifier;
+ } else {
+ throw new IllegalArgumentException("Invalid modifier '"
+ + parts[i] + "'");
+ }
}
+ return new ShortcutAction(data.length == 2 ? data[1] : null,
+ keyCode, modifiers);
+ } catch (Exception e) {
+ throw new ConversionException("Invalid shortcut '" + value + "'", e);
}
- return new ShortcutAction(data.length == 2 ? data[1] : null, keyCode,
- modifiers);
}
@Override