diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-06-01 09:32:37 +0300 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-06-01 09:34:46 +0300 |
commit | 6677046e5e23667403b8fb0499a618495796e1c8 (patch) | |
tree | 0d4bb0bf40b16be2c9e7e6158729f5bd3ff54d22 /server/src/com | |
parent | cf563c053fdf0d6b2991eba98e1cc118a8e74e54 (diff) | |
parent | 5b92ec790e3d3949992275d54ee8ac61ad94c157 (diff) | |
download | vaadin-framework-6677046e5e23667403b8fb0499a618495796e1c8.tar.gz vaadin-framework-6677046e5e23667403b8fb0499a618495796e1c8.zip |
Merge remote-tracking branch 'origin/master' into grid-unbuffered-editor
This also refactors some event handling logic.
Change-Id: I9cf96381650076da01686c5d964eb3e7e92c24c0
Diffstat (limited to 'server/src/com')
41 files changed, 578 insertions, 288 deletions
diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index e645ec60f7..9d18736ba8 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -50,6 +50,7 @@ import com.vaadin.server.KeyMapper; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.grid.DetailsConnectorChange; +import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.util.SharedUtil; @@ -135,6 +136,15 @@ public class RpcDataProviderExtension extends AbstractExtension { return String.valueOf(rollingIndex++); } + /** + * Gets the key for a given item id. Creates a new key mapping if no + * existing mapping was found for the given item id. + * + * @since 7.5.0 + * @param itemId + * the item id to get the key for + * @return the key for the given item id + */ public String getKey(Object itemId) { String key = itemIdToKey.get(itemId); if (key == null) { @@ -1124,7 +1134,7 @@ public class RpcDataProviderExtension extends AbstractExtension { Object propertyId = column.getPropertyId(); cellReference.set(propertyId); String style = generator.getStyle(cellReference); - if (style != null) { + if (style != null && !style.isEmpty()) { if (cellStyles == null) { cellStyles = Json.createObject(); } @@ -1142,7 +1152,7 @@ public class RpcDataProviderExtension extends AbstractExtension { private void setGeneratedRowStyles(RowStyleGenerator generator, JsonObject rowObject) { String rowStyle = generator.getStyle(rowReference); - if (rowStyle != null) { + if (rowStyle != null && !rowStyle.isEmpty()) { rowObject.put(GridState.JSONKEY_ROWSTYLE, rowStyle); } } @@ -1428,6 +1438,11 @@ public class RpcDataProviderExtension extends AbstractExtension { return visibleDetails.contains(itemId); } + /** + * Refreshes all visible detail sections. + * + * @since 7.5.0 + */ public void refreshDetails() { for (Object itemId : ImmutableSet.copyOf(visibleDetails)) { detailComponentManager.refresh(itemId); @@ -1443,7 +1458,12 @@ public class RpcDataProviderExtension extends AbstractExtension { return container.indexOfId(itemId); } - /** Gets the detail component manager for this data provider */ + /** + * Gets the detail component manager for this data provider + * + * @since 7.5.0 + * @return the detail component manager + * */ public DetailComponentManager getDetailComponentManager() { return detailComponentManager; } diff --git a/server/src/com/vaadin/data/util/MethodProperty.java b/server/src/com/vaadin/data/util/MethodProperty.java index 853f68b711..83279afa53 100644 --- a/server/src/com/vaadin/data/util/MethodProperty.java +++ b/server/src/com/vaadin/data/util/MethodProperty.java @@ -26,6 +26,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.data.Property; +import com.vaadin.shared.util.SharedUtil; import com.vaadin.util.SerializerHelper; /** @@ -189,11 +190,7 @@ public class MethodProperty<T> extends AbstractProperty<T> { // Assure that the first letter is upper cased (it is a common // mistake to write firstName, not FirstName). - if (Character.isLowerCase(beanPropertyName.charAt(0))) { - final char[] buf = beanPropertyName.toCharArray(); - buf[0] = Character.toUpperCase(buf[0]); - beanPropertyName = new String(buf); - } + beanPropertyName = SharedUtil.capitalize(beanPropertyName); // Find the get method getMethod = null; @@ -534,8 +531,7 @@ public class MethodProperty<T> extends AbstractProperty<T> { */ static Method initGetterMethod(String propertyName, final Class<?> beanClass) throws NoSuchMethodException { - propertyName = propertyName.substring(0, 1).toUpperCase() - + propertyName.substring(1); + propertyName = SharedUtil.capitalize(propertyName); Method getMethod = null; try { @@ -772,4 +768,5 @@ public class MethodProperty<T> extends AbstractProperty<T> { private static final Logger getLogger() { return Logger.getLogger(MethodProperty.class.getName()); } + } diff --git a/server/src/com/vaadin/data/util/NestedMethodProperty.java b/server/src/com/vaadin/data/util/NestedMethodProperty.java index 0493812861..29fe62f845 100644 --- a/server/src/com/vaadin/data/util/NestedMethodProperty.java +++ b/server/src/com/vaadin/data/util/NestedMethodProperty.java @@ -26,6 +26,7 @@ import java.util.List; import com.vaadin.data.Property; import com.vaadin.data.util.MethodProperty.MethodException; +import com.vaadin.shared.util.SharedUtil; /** * Nested accessor based property for a bean. @@ -164,11 +165,8 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> { try { // Assure that the first letter is upper cased (it is a common // mistake to write firstName, not FirstName). - if (Character.isLowerCase(lastSimplePropertyName.charAt(0))) { - final char[] buf = lastSimplePropertyName.toCharArray(); - buf[0] = Character.toUpperCase(buf[0]); - lastSimplePropertyName = new String(buf); - } + lastSimplePropertyName = SharedUtil + .capitalize(lastSimplePropertyName); setMethod = lastClass.getMethod("set" + lastSimplePropertyName, new Class[] { type }); diff --git a/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java b/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java index a84a3575f7..495bed74f8 100644 --- a/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java +++ b/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java @@ -34,6 +34,8 @@ import java.util.Locale; * {@link String} for token types. Other constructors allow to configure * delimiter and token types. * + * @since 7.5.0 + * * @author Vaadin Ltd */ public class StringToCollectionConverter implements diff --git a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java index c0c660c084..f07b7ecc58 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java @@ -1012,9 +1012,15 @@ public class SQLContainer implements Container, Container.Filterable, queryDelegate.beginTransaction(); /* Perform buffered deletions */ for (RowItem item : removedItems.values()) { - if (!queryDelegate.removeRow(item)) { + try { + if (!queryDelegate.removeRow(item)) { + throw new SQLException( + "Removal failed for row with ID: " + + item.getId()); + } + } catch (IllegalArgumentException e) { throw new SQLException("Removal failed for row with ID: " - + item.getId()); + + item.getId(), e); } } /* Perform buffered modifications */ diff --git a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java index bb000bd8f5..9a41766a31 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java @@ -213,8 +213,8 @@ public class TableQuery extends AbstractTransactionalQuery implements } finally { try { if (r != null) { - releaseConnection(r.getStatement().getConnection(), - r.getStatement(), r); + // Do not release connection, it is done in commit() + releaseConnection(null, r.getStatement(), r); } } finally { if (shouldCloseTransaction) { @@ -774,8 +774,8 @@ public class TableQuery extends AbstractTransactionalQuery implements } finally { try { if (rs != null) { - releaseConnection(rs.getStatement().getConnection(), - rs.getStatement(), rs); + // Do not release connection, it is done in commit() + releaseConnection(null, rs.getStatement(), rs); } } finally { if (shouldCloseTransaction) { diff --git a/server/src/com/vaadin/event/ShortcutAction.java b/server/src/com/vaadin/event/ShortcutAction.java index 32b909e9f2..09accae1c7 100644 --- a/server/src/com/vaadin/event/ShortcutAction.java +++ b/server/src/com/vaadin/event/ShortcutAction.java @@ -17,8 +17,6 @@ package com.vaadin.event; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -239,42 +237,6 @@ public class ShortcutAction extends Action { } /** - * Checks whether the shortcut can be triggered with the given combination - * of keys. - * - * @param keyCode - * potential match for the {@link KeyCode} that this shortcut - * reacts to - * @param modifierKeys - * (optional) potential matches for the {@link ModifierKey}s - * required for this shortcut to react - * @return <code>true</code> if keyCode and modifierKeys are a match, - * <code>false</code> otherwise - */ - public boolean isTriggeredBy(int keyCode, int... modifierKeys) { - boolean result = false; - if (keyCode == this.keyCode) { - if (modifierKeys == null) { - result = (modifiers == null); - } else if (modifiers != null) { - List<Integer> modifierList = new ArrayList<Integer>(); - for (int modifier : modifiers) { - modifierList.add(modifier); - } - for (int modifierKey : modifierKeys) { - if (modifierList.contains(modifierKey)) { - modifierList.remove(modifierKey); - } else { - return false; - } - } - result = modifierList.isEmpty(); - } - } - return result; - } - - /** * Key codes that can be used for shortcuts * */ diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 87b61c9623..0655b482ed 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -180,22 +180,40 @@ public abstract class AbstractClientConnector implements ClientConnector, * RPC interface implementation. Also used to deduce the type. */ protected <T extends ServerRpc> void registerRpc(T implementation) { + // Search upwards until an interface is found. It must be found as T + // extends ServerRpc Class<?> cls = implementation.getClass(); - Class<?>[] interfaces = cls.getInterfaces(); - while (interfaces.length == 0) { - // Search upwards until an interface is found. It must be found as T - // extends ServerRpc + Class<ServerRpc> serverRpcClass = getServerRpcInterface(cls); + + while (cls != null && serverRpcClass == null) { cls = cls.getSuperclass(); - interfaces = cls.getInterfaces(); + serverRpcClass = getServerRpcInterface(cls); } - if (interfaces.length != 1 - || !(ServerRpc.class.isAssignableFrom(interfaces[0]))) { + + if (serverRpcClass == null) { throw new RuntimeException( - "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface"); + "No interface T extends ServerRpc found in the class hierarchy."); + } + + registerRpc(implementation, serverRpcClass); + } + + @SuppressWarnings("unchecked") + private Class<ServerRpc> getServerRpcInterface(Class<?> implementationClass) { + Class<ServerRpc> serverRpcClass = null; + if (implementationClass != null) { + for (Class<?> candidateInterface : implementationClass + .getInterfaces()) { + if (ServerRpc.class.isAssignableFrom(candidateInterface)) { + if (serverRpcClass != null) { + throw new RuntimeException( + "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface"); + } + serverRpcClass = (Class<ServerRpc>) candidateInterface; + } + } } - @SuppressWarnings("unchecked") - Class<T> type = (Class<T>) interfaces[0]; - registerRpc(implementation, type); + return serverRpcClass; } /** diff --git a/server/src/com/vaadin/server/SystemMessages.java b/server/src/com/vaadin/server/SystemMessages.java index 51e9da5800..51cc7d497d 100644 --- a/server/src/com/vaadin/server/SystemMessages.java +++ b/server/src/com/vaadin/server/SystemMessages.java @@ -224,35 +224,6 @@ public class SystemMessages implements Serializable { } /** - * @return null to reload the application after out of sync message. - */ - public String getOutOfSyncURL() { - return outOfSyncURL; - } - - /** - * @return true to enable showing out of sync message - */ - public boolean isOutOfSyncNotificationEnabled() { - return outOfSyncNotificationEnabled; - } - - /** - * @return "Out of sync" - */ - public String getOutOfSyncCaption() { - return (outOfSyncNotificationEnabled ? outOfSyncCaption : null); - } - - /** - * @return "Something has caused us to be out of sync with the server.<br/> - * Take note of any unsaved data, and <u>click here</u> to re-sync." - */ - public String getOutOfSyncMessage() { - return (outOfSyncNotificationEnabled ? outOfSyncMessage : null); - } - - /** * Returns the URL the user should be redirected to after dismissing the * "you have to enable your cookies" message. Typically null. * diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index b30f6acf16..7aada2402d 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -871,9 +871,8 @@ public class VaadinServlet extends HttpServlet implements Constants { if (allowServePrecompressedResource(request, urlStr)) { // try to serve a precompressed version if available - URL url = new URL(urlStr + ".gz"); - connection = url.openConnection(); try { + connection = new URL(urlStr + ".gz").openConnection(); is = connection.getInputStream(); // set gzip headers response.setHeader("Content-Encoding", "gzip"); diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java index 9e25f1d8ff..c01c74e5cd 100644 --- a/server/src/com/vaadin/server/communication/PushRequestHandler.java +++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java @@ -145,7 +145,7 @@ public class PushRequestHandler implements RequestHandler, */ static AtmosphereFramework initAtmosphere( final ServletConfig vaadinServletConfig) { - AtmosphereFramework atmosphere = new AtmosphereFramework() { + AtmosphereFramework atmosphere = new AtmosphereFramework(false, false) { @Override protected void analytics() { // Overridden to disable version number check diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java index 450c11f5c4..65fb144810 100644 --- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java +++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java @@ -76,13 +76,14 @@ public class ServerRpcHandler implements Serializable { private final JsonArray invocations; private final int syncId; private final JsonObject json; + private final boolean resynchronize; public RpcRequest(String jsonString, VaadinRequest request) { json = JsonUtil.parse(jsonString); JsonValue token = json.get(ApplicationConstants.CSRF_TOKEN); if (token == null) { - this.csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE; + csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE; } else { String csrfToken = token.asString(); if (csrfToken.equals("")) { @@ -98,6 +99,14 @@ public class ServerRpcHandler implements Serializable { } else { syncId = -1; } + + if (json.hasKey(ApplicationConstants.RESYNCHRONIZE_ID)) { + resynchronize = json + .getBoolean(ApplicationConstants.RESYNCHRONIZE_ID); + } else { + resynchronize = false; + } + invocations = json.getArray(ApplicationConstants.RPC_INVOCATIONS); } @@ -131,6 +140,15 @@ public class ServerRpcHandler implements Serializable { } /** + * Checks if this is a request to resynchronize the client side + * + * @return true if this is a resynchronization request, false otherwise + */ + public boolean isResynchronize() { + return resynchronize; + } + + /** * Gets the entire request in JSON format, as it was received from the * client. * <p> @@ -186,6 +204,10 @@ public class ServerRpcHandler implements Serializable { ui.getConnectorTracker().cleanConcurrentlyRemovedConnectorIds( rpcRequest.getSyncId()); + + if (rpcRequest.isResynchronize()) { + ui.getSession().getCommunicationManager().repaintAll(ui); + } } /** diff --git a/server/src/com/vaadin/server/communication/UidlRequestHandler.java b/server/src/com/vaadin/server/communication/UidlRequestHandler.java index 6e338c5773..33a3669b7f 100644 --- a/server/src/com/vaadin/server/communication/UidlRequestHandler.java +++ b/server/src/com/vaadin/server/communication/UidlRequestHandler.java @@ -80,10 +80,12 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements // repaint requested or session has timed out and new one is created boolean repaintAll; - // TODO PUSH repaintAll, analyzeLayouts should be + // TODO PUSH analyzeLayouts should be // part of the message payload to make the functionality transport // agnostic + // Resynchronize is sent in the payload but will still support the + // parameter also for compatibility reasons repaintAll = (request .getParameter(ApplicationConstants.URL_PARAMETER_REPAINT_ALL) != null); diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index fd8a72ee23..27d97d5e03 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -405,6 +405,26 @@ public abstract class AbstractComponent extends AbstractClientConnector } } + /** + * Returns the explicitly set immediate value. + * + * @return the explicitly set immediate value or null if + * {@link #setImmediate(boolean)} has not been explicitly invoked + */ + protected Boolean getExplicitImmediateValue() { + return explicitImmediateValue; + } + + /** + * Returns the immediate mode of the component. + * <p> + * Certain operations such as adding a value change listener will set the + * component into immediate mode if {@link #setImmediate(boolean)} has not + * been explicitly called with false. + * + * @return true if the component is in immediate mode (explicitly or + * implicitly set), false if the component if not in immediate mode + */ public boolean isImmediate() { if (explicitImmediateValue != null) { return explicitImmediateValue; diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 5c02c9e5fb..cf14d1cb96 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -1745,6 +1745,24 @@ public abstract class AbstractField<T> extends AbstractComponent implements } } + /** + * {@inheritDoc} + * <p> + * Fields are automatically set to immediate if validators have been added. + */ + @Override + public boolean isImmediate() { + if (getExplicitImmediateValue() != null) { + return getExplicitImmediateValue(); + } + // Make field immediate when there is some kind of validation present + // (validator or required). This will avoid the UI being in a wrong + // state, e.g. user entered valid data but old validation error is still + // shown + return super.isImmediate() || !getValidators().isEmpty() + || isRequired(); + } + /* * (non-Javadoc) * diff --git a/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java b/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java index 767ae66515..0854ffe9c2 100644 --- a/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java +++ b/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java @@ -298,7 +298,9 @@ public abstract class AbstractSingleComponentContainer extends * contains multiple child elements, a DesignException is thrown. This * method should be overridden by subclasses whose design may contain * non-content child elements. - * + * + * @since 7.5.0 + * * @param children * the child elements of the design that is being read * @param context diff --git a/server/src/com/vaadin/ui/CustomLayout.java b/server/src/com/vaadin/ui/CustomLayout.java index ceb47e1e7a..d64f70e95e 100644 --- a/server/src/com/vaadin/ui/CustomLayout.java +++ b/server/src/com/vaadin/ui/CustomLayout.java @@ -74,6 +74,8 @@ public class CustomLayout extends AbstractLayout implements LegacyComponent { * for setting the appropriate fields. Either * {@link #setTemplateName(String)}, that makes layout fetch the template * from theme, or {@link #setTemplateContents(String)}. + * + * @since 7.5.0 */ public CustomLayout() { setWidth(100, Unit.PERCENTAGE); diff --git a/server/src/com/vaadin/ui/DragAndDropWrapper.java b/server/src/com/vaadin/ui/DragAndDropWrapper.java index b813973861..ba912f15dc 100644 --- a/server/src/com/vaadin/ui/DragAndDropWrapper.java +++ b/server/src/com/vaadin/ui/DragAndDropWrapper.java @@ -191,6 +191,8 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, /** * This is an internal constructor. Use * {@link DragAndDropWrapper#DragAndDropWrapper(Component)} instead. + * + * @since 7.5.0 */ @Deprecated public DragAndDropWrapper() { diff --git a/server/src/com/vaadin/ui/Flash.java b/server/src/com/vaadin/ui/Flash.java index 2d0f188b84..6e99d2fe35 100644 --- a/server/src/com/vaadin/ui/Flash.java +++ b/server/src/com/vaadin/ui/Flash.java @@ -15,6 +15,7 @@ */ package com.vaadin.ui; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -236,7 +237,15 @@ public class Flash extends AbstractEmbedded { @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); + + // Parameters, in alphabetic order + ArrayList<String> paramNames = new ArrayList<String>(); for (String param : getParameterNames()) { + paramNames.add(param); + } + + Collections.sort(paramNames); + for (String param : paramNames) { design.appendElement("parameter").attr("name", param) .attr("value", getParameter(param)); } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 43e82560df..77785c301b 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -3270,14 +3270,16 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, return getState().hidable; } - /* + /** * Writes the design attributes for this column into given element. * * @since 7.5.0 * - * @param design Element to write attributes into + * @param design + * Element to write attributes into * - * @param designContext the design context + * @param designContext + * the design context */ protected void writeDesign(Element design, DesignContext designContext) { Attributes attributes = design.attributes(); @@ -3367,20 +3369,27 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * An abstract base class for server-side Grid renderers. * {@link com.vaadin.client.widget.grid.Renderer Grid renderers}. This class - * currently extends the AbstractExtension superclass, but this fact should - * be regarded as an implementation detail and subject to change in a future - * major or minor Vaadin revision. + * currently extends the AbstractGridExtension superclass, but this fact + * should be regarded as an implementation detail and subject to change in a + * future major or minor Vaadin revision. * * @param <T> * the type this renderer knows how to present */ - public static abstract class AbstractRenderer<T> extends AbstractExtension - implements Renderer<T> { + public static abstract class AbstractRenderer<T> extends + AbstractGridExtension implements Renderer<T> { private final Class<T> presentationType; - protected AbstractRenderer(Class<T> presentationType) { + private final String nullRepresentation; + + protected AbstractRenderer(Class<T> presentationType, String nullRepresentation) { this.presentationType = presentationType; + this.nullRepresentation = nullRepresentation; + } + + protected AbstractRenderer(Class<T> presentationType) { + this(presentationType, null); } /** @@ -3410,7 +3419,19 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public JsonValue encode(T value) { - return encode(value, getPresentationType()); + if (value == null) { + return encode(getNullRepresentation(), String.class); + } else { + return encode(value, getPresentationType()); + } + } + + /** + * Null representation for the renderer + * @return a textual representation of {@code null} + */ + protected String getNullRepresentation() { + return nullRepresentation; } /** @@ -3433,6 +3454,33 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, return JsonCodec.encode(value, null, type, getUI().getConnectorTracker()).getEncodedValue(); } + } + + /** + * An abstract base class for server-side Grid extensions. + * + * @since 7.5 + */ + public static abstract class AbstractGridExtension extends + AbstractExtension { + + /** + * Constructs a new Grid extension. + */ + public AbstractGridExtension() { + super(); + } + + /** + * Constructs a new Grid extension and extends given Grid. + * + * @param grid + * a grid instance + */ + public AbstractGridExtension(Grid grid) { + super(); + extend(grid); + } /** * Gets the item id for a row key. @@ -4416,6 +4464,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * added for any property id in propertyIds that has no corresponding column * in this Grid. * + * @since 7.5.0 + * * @param propertyIds * properties in the desired column order */ @@ -5480,7 +5530,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public Iterator<Component> iterator() { - List<Component> componentList = new ArrayList<Component>(); + // This is a hash set to avoid adding header/footer components inside + // merged cells multiple times + LinkedHashSet<Component> componentList = new LinkedHashSet<Component>(); Header header = getHeader(); for (int i = 0; i < header.getRowCount(); ++i) { @@ -5797,6 +5849,13 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, column.getState().editorConnector = getEditorField(column .getPropertyId()); } + + // Must ensure that all fields, recursively, are sent to the client + // This is needed because the fields are hidden using isRendered + for (Field<?> f : getEditorFields()) { + f.markAsDirtyRecursive(); + } + } private void setEditorField(Object propertyId, Field<?> field) { @@ -6184,7 +6243,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, return datasourceExtension.isDetailsVisible(itemId); } - protected SelectionMode getDefaultSelectionMode() { + private static SelectionMode getDefaultSelectionMode() { return SelectionMode.SINGLE; } diff --git a/server/src/com/vaadin/ui/PasswordField.java b/server/src/com/vaadin/ui/PasswordField.java index 1894804775..ff3b1fea1f 100644 --- a/server/src/com/vaadin/ui/PasswordField.java +++ b/server/src/com/vaadin/ui/PasswordField.java @@ -113,4 +113,10 @@ public class PasswordField extends AbstractTextField { DesignAttributeHandler.writeAttribute("value", attr, getValue(), def.getValue(), String.class); } + + @Override + public void clear() { + setValue(""); + } + } diff --git a/server/src/com/vaadin/ui/PopupView.java b/server/src/com/vaadin/ui/PopupView.java index 12034cb56c..73c93b29f3 100644 --- a/server/src/com/vaadin/ui/PopupView.java +++ b/server/src/com/vaadin/ui/PopupView.java @@ -69,6 +69,8 @@ public class PopupView extends AbstractComponent implements HasComponents { /** * This is an internal constructor. Use * {@link PopupView#PopupView(String, Component)}Â instead. + * + * @since 7.5.0 */ @Deprecated public PopupView() { @@ -106,6 +108,8 @@ public class PopupView extends AbstractComponent implements HasComponents { /** * Creates a Content from given text representation and popup content. * + * @since 7.5.0 + * * @param minimizedValue * text representation when popup is hidden * @param popupContent diff --git a/server/src/com/vaadin/ui/ProgressBar.java b/server/src/com/vaadin/ui/ProgressBar.java index c288695ae1..89baac1e64 100644 --- a/server/src/com/vaadin/ui/ProgressBar.java +++ b/server/src/com/vaadin/ui/ProgressBar.java @@ -37,11 +37,13 @@ import com.vaadin.ui.declarative.DesignContext; public class ProgressBar extends AbstractField<Float> implements Property.Viewer, Property.ValueChangeListener { + private static final float DEFAULT_VALUE = 0f; + /** * Creates a new progress bar initially set to 0% progress. */ public ProgressBar() { - this(0); + this(DEFAULT_VALUE); } /** @@ -170,4 +172,15 @@ public class ProgressBar extends AbstractField<Float> implements DesignAttributeHandler.writeAttribute("value", design.attributes(), getValue(), defaultValue, Float.class); } + + @Override + public void clear() { + setValue(DEFAULT_VALUE); + } + + @Override + public boolean isEmpty() { + return super.isEmpty() || getValue() == DEFAULT_VALUE; + + } } diff --git a/server/src/com/vaadin/ui/RichTextArea.java b/server/src/com/vaadin/ui/RichTextArea.java index 763e42ad8b..7c23cce5cb 100644 --- a/server/src/com/vaadin/ui/RichTextArea.java +++ b/server/src/com/vaadin/ui/RichTextArea.java @@ -293,6 +293,11 @@ public class RichTextArea extends AbstractField<String> implements } @Override + public void clear() { + setValue(""); + } + + @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); setValue(design.html()); diff --git a/server/src/com/vaadin/ui/Slider.java b/server/src/com/vaadin/ui/Slider.java index 40a4047d53..2e00628eb0 100644 --- a/server/src/com/vaadin/ui/Slider.java +++ b/server/src/com/vaadin/ui/Slider.java @@ -104,9 +104,9 @@ public class Slider extends AbstractField<Double> { */ public Slider(double min, double max, int resolution) { this(); - setMin(min); - setMax(max); setResolution(resolution); + setMax(max); + setMin(min); } /** @@ -167,14 +167,15 @@ public class Slider extends AbstractField<Double> { * The new maximum slider value */ public void setMax(double max) { - getState().maxValue = max; + double roundedMax = getRoundedValue(max); + getState().maxValue = roundedMax; - if (getMin() > max) { - getState().minValue = max; + if (getMin() > roundedMax) { + getState().minValue = roundedMax; } - if (getValue() > max) { - setValue(max); + if (getValue() > roundedMax) { + setValue(roundedMax); } } @@ -195,14 +196,15 @@ public class Slider extends AbstractField<Double> { * The new minimum slider value */ public void setMin(double min) { - getState().minValue = min; + double roundedMin = getRoundedValue(min); + getState().minValue = roundedMin; - if (getMax() < min) { - getState().maxValue = min; + if (getMax() < roundedMin) { + getState().maxValue = roundedMin; } - if (getValue() < min) { - setValue(min); + if (getValue() < roundedMin) { + setValue(roundedMin); } } @@ -268,28 +270,28 @@ public class Slider extends AbstractField<Double> { */ @Override protected void setValue(Double value, boolean repaintIsNotNeeded) { - final double v = value.doubleValue(); - final int resolution = getResolution(); - double newValue; - - if (resolution > 0) { - // Round up to resolution - newValue = Math.floor(v * Math.pow(10, resolution)); - newValue = newValue / Math.pow(10, resolution); - if (getMin() > newValue || getMax() < newValue) { - throw new ValueOutOfBoundsException(newValue); - } - } else { - newValue = (int) v; - if (getMin() > newValue || getMax() < newValue) { - throw new ValueOutOfBoundsException(newValue); - } + double newValue = getRoundedValue(value); + + if (getMin() > newValue || getMax() < newValue) { + throw new ValueOutOfBoundsException(newValue); } getState().value = newValue; super.setValue(newValue, repaintIsNotNeeded); } + private double getRoundedValue(Double value) { + final double v = value.doubleValue(); + final int resolution = getResolution(); + + double ratio = Math.pow(10, resolution); + if(v >= 0) { + return Math.floor(v * ratio) / ratio; + } else { + return Math.ceil(v * ratio) / ratio; + } + } + @Override public void setValue(Double newFieldValue) { super.setValue(newFieldValue); diff --git a/server/src/com/vaadin/ui/TextArea.java b/server/src/com/vaadin/ui/TextArea.java index c8103f9c5b..b4dfb209e8 100644 --- a/server/src/com/vaadin/ui/TextArea.java +++ b/server/src/com/vaadin/ui/TextArea.java @@ -159,4 +159,10 @@ public class TextArea extends AbstractTextField { super.writeDesign(design, designContext); design.html(getValue()); } + + @Override + public void clear() { + setValue(""); + } + } diff --git a/server/src/com/vaadin/ui/Tree.java b/server/src/com/vaadin/ui/Tree.java index 48f4e54831..aac827e5b5 100644 --- a/server/src/com/vaadin/ui/Tree.java +++ b/server/src/com/vaadin/ui/Tree.java @@ -273,7 +273,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, * @param sendChildTree * flag to indicate if client needs subtree or not (may be * cached) - * @return True iff the expand operation succeeded + * @return True if the expand operation succeeded */ private boolean expandItem(Object itemId, boolean sendChildTree) { diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 8dd600ddd0..b16d7e32d3 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -16,6 +16,8 @@ package com.vaadin.ui; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; @@ -67,6 +69,7 @@ import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.shared.ui.ui.UIServerRpc; import com.vaadin.shared.ui.ui.UIState; import com.vaadin.ui.Component.Focusable; +import com.vaadin.ui.declarative.Design; import com.vaadin.util.ConnectorHelper; import com.vaadin.util.CurrentInstance; @@ -211,6 +214,32 @@ public abstract class UI extends AbstractSingleComponentContainer implements json.toString()); } + @Override + public void showServerDesign(Connector connector) { + if (!(connector instanceof Component)) { + getLogger().severe( + "Tried to output declarative design for " + connector + + ", which is not a component"); + return; + } + if (connector instanceof UI) { + // We want to see the content of the UI, so we can add it to + // another UI or component container + connector = ((UI) connector).getContent(); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + Design.write((Component) connector, baos); + getLogger().info( + "Design for " + connector + + " requested from debug window:\n" + + baos.toString("UTF-8")); + } catch (IOException e) { + getLogger().log(Level.WARNING, + "Error producing design for " + connector, e); + } + + } }; /** diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index e7764ffd8d..61664fc95d 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -21,9 +21,6 @@ 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; @@ -133,7 +130,6 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, super(caption, content); registerRpc(rpc); setSizeUndefined(); - setCloseShortcut(ShortcutAction.KeyCode.ESCAPE); } /* ********************************************************************* */ @@ -820,48 +816,14 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, /* * Actions */ - private LinkedHashSet<CloseShortcut> closeShortcuts = new LinkedHashSet<CloseShortcut>(); - - protected Collection<CloseShortcut> getCloseShortcuts() { - return Collections.unmodifiableCollection(closeShortcuts); - } - - /** - * Adds a keyboard shortcut for closing the window when user presses the - * given {@link KeyCode} and (optional) {@link ModifierKey}s.<br/> - * Note that this shortcut only reacts while the window has focus, closing - * itself - if you want to close a window from a UI, use - * {@link UI#addAction(com.vaadin.event.Action)} of the UI instead. - * <p> - * If there is a prior CloseShortcut with the same keycode and modifiers, - * that gets removed before the new one is added. Prior CloseShortcuts with - * differing keycodes or modifiers are not affected. - * - * @param keyCode - * the keycode for invoking the shortcut - * @param modifiers - * the (optional) modifiers for invoking the shortcut, null for - * none - */ - public void addCloseShortcut(int keyCode, int... modifiers) { - // make sure there are no duplicates - removeCloseShortcut(keyCode, modifiers); - CloseShortcut closeShortcut = new CloseShortcut(this, keyCode, - modifiers); - closeShortcuts.add(closeShortcut); - addAction(closeShortcut); - } + protected CloseShortcut closeShortcut; /** - * Sets the keyboard shortcut for closing the window when user presses the - * given {@link KeyCode} and (optional) {@link ModifierKey}s.<br/> + * Makes is possible to close the window by pressing the given + * {@link KeyCode} and (optional) {@link ModifierKey}s.<br/> * Note that this shortcut only reacts while the window has focus, closing * itself - if you want to close a window from a UI, use * {@link UI#addAction(com.vaadin.event.Action)} of the UI instead. - * <p> - * If there are any prior CloseShortcuts when this method is called those - * get removed before the new one is added. <b>NOTE: this also removes the - * default shortcut that is added for accessibility purposes.</b> * * @param keyCode * the keycode for invoking the shortcut @@ -870,61 +832,22 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * none */ public void setCloseShortcut(int keyCode, int... modifiers) { - removeCloseShortcuts(); - addCloseShortcut(keyCode, modifiers); - } - - /** - * Removes a keyboard shortcut previously set with - * {@link #setCloseShortcut(int, int...)} or - * {@link #addCloseShortcut(int, int...)}. - * - * @param keyCode - * the keycode for invoking the shortcut - * @param modifiers - * the (optional) modifiers for invoking the shortcut, null for - * none - */ - public void removeCloseShortcut(int keyCode, int... modifiers) { - for (CloseShortcut closeShortcut : closeShortcuts) { - if (closeShortcut.isTriggeredBy(keyCode, modifiers)) { - removeAction(closeShortcut); - closeShortcuts.remove(closeShortcut); - break; - } + if (closeShortcut != null) { + removeAction(closeShortcut); } + closeShortcut = new CloseShortcut(this, keyCode, modifiers); + addAction(closeShortcut); } /** - * @deprecated use {@link #resetCloseShortcuts()} instead, or - * {@link #removeCloseShortcuts()} if you also want to get rid - * of the default shortcut + * Removes the keyboard shortcut previously set with + * {@link #setCloseShortcut(int, int...)}. */ - @Deprecated public void removeCloseShortcut() { - resetCloseShortcuts(); - } - - /** - * Removes all the keyboard shortcuts previously set with - * {@link #setCloseShortcut(int, int...)} or - * {@link #addCloseShortcut(int, int...)} and re-adds the default shortcut - * {@link KeyCode.ESCAPE}. - */ - public void resetCloseShortcuts() { - setCloseShortcut(ShortcutAction.KeyCode.ESCAPE); - } - - /** - * Removes all the keyboard shortcuts previously set with - * {@link #setCloseShortcut(int, int...)} or - * {@link #addCloseShortcut(int, int...)}. - */ - public void removeCloseShortcuts() { - for (CloseShortcut closeShortcut : closeShortcuts) { + if (closeShortcut != null) { removeAction(closeShortcut); + closeShortcut = null; } - closeShortcuts.clear(); } /** @@ -1391,8 +1314,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } private CloseShortcut getCloseShortcut() { - Iterator<CloseShortcut> i = getCloseShortcuts().iterator(); - return i.hasNext() ? i.next() : null; + return closeShortcut; } @Override diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java index d6cb970ce8..0d68c22ea0 100644 --- a/server/src/com/vaadin/ui/declarative/DesignContext.java +++ b/server/src/com/vaadin/ui/declarative/DesignContext.java @@ -171,6 +171,8 @@ public class DesignContext implements Serializable { * component was mapped to some string s different from localId, the mapping * from s to component is removed. * + * @since 7.5.0 + * * @param component * The component whose local id is to be set. * @param localId @@ -186,7 +188,9 @@ public class DesignContext implements Serializable { } /** - * Returns the local id for a component + * Returns the local id for a component. + * + * @since 7.5.0 * * @param component * The component whose local id to get. diff --git a/server/src/com/vaadin/ui/declarative/FieldBinder.java b/server/src/com/vaadin/ui/declarative/FieldBinder.java index bd906682fa..577e9f5515 100644 --- a/server/src/com/vaadin/ui/declarative/FieldBinder.java +++ b/server/src/com/vaadin/ui/declarative/FieldBinder.java @@ -204,11 +204,14 @@ public class FieldBinder implements Serializable { } return true; } catch (IllegalAccessException e) { - throw new FieldBindingException("Field binding failed", e); + throw new FieldBindingException("Field binding failed for " + + identifier, e); } catch (IllegalArgumentException e) { - throw new FieldBindingException("Field binding failed", e); + throw new FieldBindingException("Field binding failed for " + + identifier, e); } catch (InvocationTargetException e) { - throw new FieldBindingException("Field binding failed", e); + throw new FieldBindingException("Field binding failed for " + + identifier, e); } } diff --git a/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java b/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java index ff73b61eb2..ffa51fedd1 100644 --- a/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java +++ b/server/src/com/vaadin/ui/declarative/converters/DesignResourceConverter.java @@ -28,8 +28,8 @@ import com.vaadin.server.FontAwesome; import com.vaadin.server.FontIcon; import com.vaadin.server.GenericFontIcon; import com.vaadin.server.Resource; +import com.vaadin.server.ResourceReference; import com.vaadin.server.ThemeResource; -import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.declarative.DesignAttributeHandler; /** @@ -109,8 +109,7 @@ public class DesignResourceConverter implements Converter<String, Resource> { @Override public String format(Resource value) throws Converter.ConversionException { - return ApplicationConstants.THEME_PROTOCOL_PREFIX - + ((ThemeResource) value).getResourceId(); + return new ResourceReference(value, null, null).getURL(); } }, FONTICON { @@ -118,8 +117,7 @@ public class DesignResourceConverter implements Converter<String, Resource> { public Resource parse(String value) { final String address = (value.split("://", 2))[1]; final String[] familyAndCode = address.split("/", 2); - final int codepoint = Integer.valueOf( - familyAndCode[1].substring(2), 16); + final int codepoint = Integer.valueOf(familyAndCode[1], 16); if (FontAwesome.FONT_FAMILY.equals(familyAndCode[0])) { try { @@ -141,9 +139,8 @@ public class DesignResourceConverter implements Converter<String, Resource> { public String format(Resource value) throws Converter.ConversionException { FontIcon icon = (FontIcon) value; - return ApplicationConstants.FONTICON_PROTOCOL_PREFIX - + icon.getFontFamily() + "/0x" - + Integer.toHexString(icon.getCodepoint()); + return new ResourceReference(icon, null, null).getURL(); + } }, @Deprecated diff --git a/server/src/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java b/server/src/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java index bed34cf6e3..104e07f02f 100644 --- a/server/src/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java +++ b/server/src/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java @@ -105,8 +105,12 @@ public abstract class AbstractJavaScriptRenderer<T> extends AbstractRenderer<T> private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( this); + protected AbstractJavaScriptRenderer(Class<T> presentationType, String nullRepresentation) { + super(presentationType, nullRepresentation); + } + protected AbstractJavaScriptRenderer(Class<T> presentationType) { - super(presentationType); + super(presentationType, null); } @Override diff --git a/server/src/com/vaadin/ui/renderers/ButtonRenderer.java b/server/src/com/vaadin/ui/renderers/ButtonRenderer.java index 0b070d3f69..a747e45713 100644 --- a/server/src/com/vaadin/ui/renderers/ButtonRenderer.java +++ b/server/src/com/vaadin/ui/renderers/ButtonRenderer.java @@ -15,6 +15,8 @@ */ package com.vaadin.ui.renderers; +import elemental.json.JsonValue; + /** * A Renderer that displays a button with a textual caption. The value of the * corresponding property is used as the caption. Click listeners can be added @@ -27,9 +29,12 @@ public class ButtonRenderer extends ClickableRenderer<String> { /** * Creates a new button renderer. + * + * @param nullRepresentation + * the textual representation of {@code null} value */ - public ButtonRenderer() { - super(String.class); + public ButtonRenderer(String nullRepresentation) { + super(String.class, nullRepresentation); } /** @@ -37,9 +42,34 @@ public class ButtonRenderer extends ClickableRenderer<String> { * * @param listener * the click listener to register + * @param nullRepresentation + * the textual representation of {@code null} value */ - public ButtonRenderer(RendererClickListener listener) { - this(); + public ButtonRenderer(RendererClickListener listener, String nullRepresentation) { + this(nullRepresentation); addClickListener(listener); } + + /** + * Creates a new button renderer. + */ + public ButtonRenderer() { + this(""); + } + + /** + * Creates a new button renderer and adds the given click listener to it. + * + * @param listener + * the click listener to register + */ + public ButtonRenderer(RendererClickListener listener) { + this(listener, ""); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } + } diff --git a/server/src/com/vaadin/ui/renderers/ClickableRenderer.java b/server/src/com/vaadin/ui/renderers/ClickableRenderer.java index 38e9acef9c..01e939bb67 100644 --- a/server/src/com/vaadin/ui/renderers/ClickableRenderer.java +++ b/server/src/com/vaadin/ui/renderers/ClickableRenderer.java @@ -103,7 +103,11 @@ public class ClickableRenderer<T> extends AbstractRenderer<T> { } protected ClickableRenderer(Class<T> presentationType) { - super(presentationType); + this(presentationType, null); + } + + protected ClickableRenderer(Class<T> presentationType, String nullRepresentation) { + super(presentationType, nullRepresentation); registerRpc(new RendererClickRpc() { @Override public void click(String rowKey, String columnId, diff --git a/server/src/com/vaadin/ui/renderers/DateRenderer.java b/server/src/com/vaadin/ui/renderers/DateRenderer.java index 9dd4d19e87..092b3f405e 100644 --- a/server/src/com/vaadin/ui/renderers/DateRenderer.java +++ b/server/src/com/vaadin/ui/renderers/DateRenderer.java @@ -41,7 +41,7 @@ public class DateRenderer extends AbstractRenderer<Date> { * representation for the default locale. */ public DateRenderer() { - this(Locale.getDefault()); + this(Locale.getDefault(), ""); } /** @@ -56,7 +56,24 @@ public class DateRenderer extends AbstractRenderer<Date> { * if {@code locale} is {@code null} */ public DateRenderer(Locale locale) throws IllegalArgumentException { - this("%s", locale); + this("%s", locale, ""); + } + + /** + * Creates a new date renderer. + * <p> + * The renderer is configured to render with the {@link Date#toString()} + * representation for the given locale. + * + * @param locale + * the locale in which to present dates + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if {@code locale} is {@code null} + */ + public DateRenderer(Locale locale, String nullRepresentation) throws IllegalArgumentException { + this("%s", locale, nullRepresentation); } /** @@ -74,7 +91,27 @@ public class DateRenderer extends AbstractRenderer<Date> { * String Syntax</a> */ public DateRenderer(String formatString) throws IllegalArgumentException { - this(formatString, Locale.getDefault()); + this(formatString, ""); + } + + /** + * Creates a new date renderer. + * <p> + * The renderer is configured to render with the given string format, as + * displayed in the default locale. + * + * @param formatString + * the format string with which to format the date + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if {@code formatString} is {@code null} + * @see <a + * href="http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax">Format + * String Syntax</a> + */ + public DateRenderer(String formatString, String nullRepresentation) throws IllegalArgumentException { + this(formatString, Locale.getDefault(), nullRepresentation); } /** @@ -95,7 +132,29 @@ public class DateRenderer extends AbstractRenderer<Date> { */ public DateRenderer(String formatString, Locale locale) throws IllegalArgumentException { - super(Date.class); + this(formatString,locale, ""); + } + /** + * Creates a new date renderer. + * <p> + * The renderer is configured to render with the given string format, as + * displayed in the given locale. + * + * @param formatString + * the format string to format the date with + * @param locale + * the locale to use + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if either argument is {@code null} + * @see <a + * href="http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax">Format + * String Syntax</a> + */ + public DateRenderer(String formatString, Locale locale, String nullRepresentation) + throws IllegalArgumentException { + super(Date.class, nullRepresentation); if (formatString == null) { throw new IllegalArgumentException("format string may not be null"); @@ -121,7 +180,20 @@ public class DateRenderer extends AbstractRenderer<Date> { * if {@code dateFormat} is {@code null} */ public DateRenderer(DateFormat dateFormat) throws IllegalArgumentException { - super(Date.class); + this(dateFormat, ""); + } + /** + * Creates a new date renderer. + * <p> + * The renderer is configured to render with he given date format. + * + * @param dateFormat + * the date format to use when rendering dates + * @throws IllegalArgumentException + * if {@code dateFormat} is {@code null} + */ + public DateRenderer(DateFormat dateFormat, String nullRepresentation) throws IllegalArgumentException { + super(Date.class, nullRepresentation); if (dateFormat == null) { throw new IllegalArgumentException("date format may not be null"); } @@ -132,9 +204,16 @@ public class DateRenderer extends AbstractRenderer<Date> { } @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } + + @Override public JsonValue encode(Date value) { String dateString; - if (dateFormat != null) { + if (value == null) { + dateString = getNullRepresentation(); + } else if (dateFormat != null) { dateString = dateFormat.format(value); } else { dateString = String.format(locale, formatString, value); diff --git a/server/src/com/vaadin/ui/renderers/HtmlRenderer.java b/server/src/com/vaadin/ui/renderers/HtmlRenderer.java index 34774b3825..df89c60ad2 100644 --- a/server/src/com/vaadin/ui/renderers/HtmlRenderer.java +++ b/server/src/com/vaadin/ui/renderers/HtmlRenderer.java @@ -16,18 +16,33 @@ package com.vaadin.ui.renderers; import com.vaadin.ui.Grid.AbstractRenderer; +import elemental.json.JsonValue; /** * A renderer for presenting HTML content. - * - * @since 7.4 + * * @author Vaadin Ltd + * @since 7.4 */ public class HtmlRenderer extends AbstractRenderer<String> { /** * Creates a new HTML renderer. + * + * @param nullRepresentation the html representation of {@code null} value + */ + public HtmlRenderer(String nullRepresentation) { + super(String.class, nullRepresentation); + } + + /** + * Creates a new HTML renderer. */ public HtmlRenderer() { - super(String.class); + this(""); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); } } diff --git a/server/src/com/vaadin/ui/renderers/ImageRenderer.java b/server/src/com/vaadin/ui/renderers/ImageRenderer.java index 4bb3671033..2fb872583e 100644 --- a/server/src/com/vaadin/ui/renderers/ImageRenderer.java +++ b/server/src/com/vaadin/ui/renderers/ImageRenderer.java @@ -39,7 +39,7 @@ public class ImageRenderer extends ClickableRenderer<Resource> { * Creates a new image renderer. */ public ImageRenderer() { - super(Resource.class); + super(Resource.class, null); } /** @@ -55,7 +55,7 @@ public class ImageRenderer extends ClickableRenderer<Resource> { @Override public JsonValue encode(Resource resource) { - if (!(resource instanceof ExternalResource || resource instanceof ThemeResource)) { + if (!(resource == null || resource instanceof ExternalResource || resource instanceof ThemeResource)) { throw new IllegalArgumentException( "ImageRenderer only supports ExternalResource and ThemeResource (" + resource.getClass().getSimpleName() + "given )"); diff --git a/server/src/com/vaadin/ui/renderers/NumberRenderer.java b/server/src/com/vaadin/ui/renderers/NumberRenderer.java index 5c30e55b17..1d4d7e0ec9 100644 --- a/server/src/com/vaadin/ui/renderers/NumberRenderer.java +++ b/server/src/com/vaadin/ui/renderers/NumberRenderer.java @@ -24,7 +24,7 @@ import elemental.json.JsonValue; /** * A renderer for presenting number values. - * + * * @since 7.4 * @author Vaadin Ltd */ @@ -35,7 +35,7 @@ public class NumberRenderer extends AbstractRenderer<Number> { /** * Creates a new number renderer. - * <p> + * <p/> * The renderer is configured to render with the number's natural string * representation in the default locale. */ @@ -45,18 +45,35 @@ public class NumberRenderer extends AbstractRenderer<Number> { /** * Creates a new number renderer. - * <p> + * <p/> * The renderer is configured to render the number as defined with the given * number format. - * + * * @param numberFormat * the number format with which to display numbers * @throws IllegalArgumentException * if {@code numberFormat} is {@code null} */ - public NumberRenderer(NumberFormat numberFormat) + public NumberRenderer(NumberFormat numberFormat) { + this(numberFormat, ""); + } + + /** + * Creates a new number renderer. + * <p/> + * The renderer is configured to render the number as defined with the given + * number format. + * + * @param numberFormat + * the number format with which to display numbers + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if {@code numberFormat} is {@code null} + */ + public NumberRenderer(NumberFormat numberFormat, String nullRepresentation) throws IllegalArgumentException { - super(Number.class); + super(Number.class, nullRepresentation); if (numberFormat == null) { throw new IllegalArgumentException("Number format may not be null"); @@ -69,10 +86,10 @@ public class NumberRenderer extends AbstractRenderer<Number> { /** * Creates a new number renderer. - * <p> + * <p/> * The renderer is configured to render with the number's natural string * representation in the given locale. - * + * * @param locale * the locale in which to display numbers * @throws IllegalArgumentException @@ -84,12 +101,29 @@ public class NumberRenderer extends AbstractRenderer<Number> { /** * Creates a new number renderer. - * <p> - * The renderer is configured to render with the given format string in the - * default locale. - * + * <p/> + * The renderer is configured to render with the number's natural string + * representation in the given locale. + * * @param formatString * the format string with which to format the number + * @param locale + * the locale in which to display numbers + * @throws IllegalArgumentException + * if {@code locale} is {@code null} + */ + public NumberRenderer(String formatString, Locale locale) throws IllegalArgumentException { + this(formatString, locale, ""); //This will call #toString() during formatting + } + + /** + * Creates a new number renderer. + * <p/> + * The renderer is configured to render with the given format string in the + * default locale. + * + * @param + * formatString the format string with which to format the number * @throws IllegalArgumentException * if {@code formatString} is {@code null} * @see <a @@ -97,15 +131,15 @@ public class NumberRenderer extends AbstractRenderer<Number> { * String Syntax</a> */ public NumberRenderer(String formatString) throws IllegalArgumentException { - this(formatString, Locale.getDefault()); + this(formatString, Locale.getDefault(), ""); } /** * Creates a new number renderer. - * <p> + * <p/> * The renderer is configured to render with the given format string in the * given locale. - * + * * @param formatString * the format string with which to format the number * @param locale @@ -116,8 +150,8 @@ public class NumberRenderer extends AbstractRenderer<Number> { * href="http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax">Format * String Syntax</a> */ - public NumberRenderer(String formatString, Locale locale) { - super(Number.class); + public NumberRenderer(String formatString, Locale locale, String nullRepresentation) { + super(Number.class, nullRepresentation); if (formatString == null) { throw new IllegalArgumentException("Format string may not be null"); @@ -135,7 +169,9 @@ public class NumberRenderer extends AbstractRenderer<Number> { @Override public JsonValue encode(Number value) { String stringValue; - if (formatString != null && locale != null) { + if (value == null) { + stringValue = getNullRepresentation(); + } else if (formatString != null && locale != null) { stringValue = String.format(locale, formatString, value); } else if (numberFormat != null) { stringValue = numberFormat.format(value); @@ -160,4 +196,9 @@ public class NumberRenderer extends AbstractRenderer<Number> { return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo); } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } } diff --git a/server/src/com/vaadin/ui/renderers/ProgressBarRenderer.java b/server/src/com/vaadin/ui/renderers/ProgressBarRenderer.java index a077cd8012..1566c47222 100644 --- a/server/src/com/vaadin/ui/renderers/ProgressBarRenderer.java +++ b/server/src/com/vaadin/ui/renderers/ProgressBarRenderer.java @@ -21,9 +21,9 @@ import elemental.json.JsonValue; /** * A renderer that represents a double values as a graphical progress bar. - * - * @since 7.4 + * * @author Vaadin Ltd + * @since 7.4 */ public class ProgressBarRenderer extends AbstractRenderer<Double> { @@ -31,13 +31,15 @@ public class ProgressBarRenderer extends AbstractRenderer<Double> { * Creates a new text renderer */ public ProgressBarRenderer() { - super(Double.class); + super(Double.class, null); } @Override public JsonValue encode(Double value) { if (value != null) { value = Math.max(Math.min(value, 1), 0); + } else { + value = 0d; } return super.encode(value); } diff --git a/server/src/com/vaadin/ui/renderers/TextRenderer.java b/server/src/com/vaadin/ui/renderers/TextRenderer.java index 3723a45f70..0045024b2f 100644 --- a/server/src/com/vaadin/ui/renderers/TextRenderer.java +++ b/server/src/com/vaadin/ui/renderers/TextRenderer.java @@ -16,6 +16,7 @@ package com.vaadin.ui.renderers; import com.vaadin.ui.Grid.AbstractRenderer; +import elemental.json.JsonValue; /** * A renderer for presenting simple plain-text string values. @@ -29,6 +30,20 @@ public class TextRenderer extends AbstractRenderer<String> { * Creates a new text renderer */ public TextRenderer() { - super(String.class); + this(""); + } + + /** + * Creates a new text renderer + * @param nullRepresentation + * the textual representation of {@code null} value + */ + public TextRenderer(String nullRepresentation) { + super(String.class, nullRepresentation); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); } } |