summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2016-09-05 12:20:36 +0300
committerLeif Åstrand <leif@vaadin.com>2016-09-06 15:07:07 +0300
commit3d3f12d92bb840a66ed6caaf992e0fe7bb3c1e77 (patch)
treed6b4f92365dff685f0576580fb7b0a9e10ec3fe7 /server
parentfbb55ac4fdf09103cf7e999b321812bea033a981 (diff)
downloadvaadin-framework-3d3f12d92bb840a66ed6caaf992e0fe7bb3c1e77.tar.gz
vaadin-framework-3d3f12d92bb840a66ed6caaf992e0fe7bb3c1e77.zip
Reimplement basic Grid renderers
Change-Id: Ic855143d23cc7735d00130806df45b6579ba0d5c
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java120
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/AbstractRenderer.java139
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java238
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java46
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java205
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java45
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/Renderer.java69
-rw-r--r--server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java47
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java2
9 files changed, 886 insertions, 25 deletions
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index 387cd22275..22b5ef8072 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -46,9 +46,12 @@ import com.vaadin.shared.ui.grid.GridConstants.Section;
import com.vaadin.shared.ui.grid.GridServerRpc;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.shared.ui.grid.HeightMode;
+import com.vaadin.ui.renderers.Renderer;
+import com.vaadin.ui.renderers.TextRenderer;
import elemental.json.Json;
import elemental.json.JsonObject;
+import elemental.json.JsonValue;
/**
* A grid component for displaying tabular data.
@@ -305,49 +308,99 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
public static class Column<T, V> extends AbstractExtension
implements DataGenerator<T> {
- private Function<T, V> valueProvider;
+ private final Function<T, ? extends V> valueProvider;
+
private Function<SortDirection, Stream<SortOrder<String>>> sortOrderProvider;
private Comparator<T> comparator;
/**
- * Constructs a new Column configuration with given header caption and
- * value provider.
+ * Constructs a new Column configuration with given header caption,
+ * renderer and value provider.
*
* @param caption
* the header caption
- * @param valueType
- * the type of value
* @param valueProvider
* the function to get values from items
+ * @param renderer
+ * the type of value
*/
- protected Column(String caption, Class<V> valueType,
- Function<T, V> valueProvider) {
+ protected Column(String caption, Function<T, ? extends V> valueProvider,
+ Renderer<V> renderer) {
Objects.requireNonNull(caption, "Header caption can't be null");
Objects.requireNonNull(valueProvider,
"Value provider can't be null");
- Objects.requireNonNull(valueType, "Value type can't be null");
+ Objects.requireNonNull(renderer, "Renderer can't be null");
+
+ ColumnState state = getState();
this.valueProvider = valueProvider;
- getState().caption = caption;
+ state.renderer = renderer;
+
+ state.caption = caption;
sortOrderProvider = d -> Stream.of();
+ // Add the renderer as a child extension of this extension, thus
+ // ensuring the renderer will be unregistered when this column is
+ // removed
+ addExtension(renderer);
+
+ Class<V> valueType = renderer.getPresentationType();
+
if (Comparable.class.isAssignableFrom(valueType)) {
comparator = (a, b) -> {
@SuppressWarnings("unchecked")
Comparable<V> comp = (Comparable<V>) valueProvider.apply(a);
return comp.compareTo(valueProvider.apply(b));
};
- getState().sortable = true;
+ state.sortable = true;
+ } else if (Number.class.isAssignableFrom(valueType)) {
+ /*
+ * Value type will be Number whenever using NumberRenderer.
+ * Provide explicit comparison support in this case even though
+ * Number itself isn't Comparable.
+ */
+ comparator = (a, b) -> {
+ return compareNumbers((Number) valueProvider.apply(a),
+ (Number) valueProvider.apply(b));
+ };
+ state.sortable = true;
} else {
- getState().sortable = false;
+ state.sortable = false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static int compareNumbers(Number a, Number b) {
+ assert a.getClass() == b.getClass();
+
+ // Most Number implementations are Comparable
+ if (a instanceof Comparable && a.getClass().isInstance(b)) {
+ return ((Comparable<Number>) a).compareTo(b);
+ } else if (a.equals(b)) {
+ return 0;
+ } else {
+ // Fall back to comparing based on potentially truncated values
+ int compare = Long.compare(a.longValue(), b.longValue());
+ if (compare == 0) {
+ // This might still produce 0 even though the values are not
+ // equals, but there's nothing more we can do about that
+ compare = Double.compare(a.doubleValue(), b.doubleValue());
+ }
+ return compare;
}
}
@Override
public void generateData(T data, JsonObject jsonObject) {
- assert getState(
- false).id != null : "No communication ID set for column "
- + getState(false).caption;
+ ColumnState state = getState(false);
+
+ String communicationId = state.id;
+
+ assert communicationId != null : "No communication ID set for column "
+ + state.caption;
+
+ @SuppressWarnings("unchecked")
+ Renderer<V> renderer = (Renderer<V>) state.renderer;
if (!jsonObject.hasKey(DataCommunicatorConstants.DATA)) {
jsonObject.put(DataCommunicatorConstants.DATA,
@@ -355,9 +408,12 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
}
JsonObject obj = jsonObject
.getObject(DataCommunicatorConstants.DATA);
- // Since we dont' have renderers yet, use a dummy toString for
- // data.
- obj.put(getState(false).id, valueProvider.apply(data).toString());
+
+ V providerValue = valueProvider.apply(data);
+
+ JsonValue rendererValue = renderer.encode(providerValue);
+
+ obj.put(communicationId, rendererValue);
}
@Override
@@ -538,23 +594,23 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
}
/**
- * Adds a new column to this {@link Grid} with given header caption and
- * value provider.
+ * Adds a new column to this {@link Grid} with given header caption,
+ * renderer and value provider.
*
* @param caption
* the header caption
- * @param valueType
- * the column value class
* @param valueProvider
* the value provider
+ * @param renderer
+ * the column value class
* @param <V>
* the column value type
*
* @return the new column
*/
- public <V> Column<T, V> addColumn(String caption, Class<V> valueType,
- Function<T, V> valueProvider) {
- Column<T, V> c = new Column<>(caption, valueType, valueProvider);
+ public <V> Column<T, V> addColumn(String caption, Function<T, ? extends V> valueProvider,
+ Renderer<V> renderer) {
+ Column<T, V> c = new Column<>(caption, valueProvider, renderer);
c.extend(this);
c.setId(columnKeys.key(c));
@@ -565,6 +621,22 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
}
/**
+ * Adds a new text column to this {@link Grid} with given header caption
+ * string value provider. The column will use a {@link TextRenderer}.
+ *
+ * @param caption
+ * the header caption
+ * @param valueProvider
+ * the value provider
+ *
+ * @return the new column
+ */
+ public Column<T, String> addColumn(String caption,
+ Function<T, String> valueProvider) {
+ return addColumn(caption, valueProvider, new TextRenderer());
+ }
+
+ /**
* Removes the given column from this {@link Grid}.
*
* @param column
diff --git a/server/src/main/java/com/vaadin/ui/renderers/AbstractRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/AbstractRenderer.java
new file mode 100644
index 0000000000..4dcf911515
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/AbstractRenderer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+import java.util.Objects;
+
+import com.vaadin.server.AbstractClientConnector;
+import com.vaadin.server.AbstractExtension;
+import com.vaadin.server.JsonCodec;
+import com.vaadin.ui.Grid.Column;
+
+import elemental.json.JsonValue;
+
+/**
+ * An abstract base class for server-side
+ * {@link com.vaadin.ui.renderers.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 version.
+ *
+ * @param <T>
+ * the type this renderer knows how to present
+ */
+public abstract class AbstractRenderer<T> extends AbstractExtension
+ implements Renderer<T> {
+ private final Class<T> presentationType;
+
+ private final String nullRepresentation;
+
+ /**
+ * Creates a new renderer with the given presentation type and null
+ * representation.
+ *
+ * @param presentationType
+ * the data type that this renderer displays, not
+ * <code>null</code>
+ * @param nullRepresentation
+ * a string that will be sent to the client instead of a regular
+ * value in case the actual cell value is <code>null</code>. May
+ * be <code>null</code>.
+ */
+ protected AbstractRenderer(Class<T> presentationType,
+ String nullRepresentation) {
+ Objects.requireNonNull(presentationType,
+ "Presentation type cannot be null");
+ this.presentationType = presentationType;
+ this.nullRepresentation = nullRepresentation;
+ }
+
+ /**
+ * Creates a new renderer with the given presentation type. No null
+ * representation will be used.
+ *
+ * @param presentationType
+ * the data type that this renderer displays, not
+ * <code>null</code>
+ */
+ protected AbstractRenderer(Class<T> presentationType) {
+ this(presentationType, null);
+ }
+
+ /**
+ * This method is inherited from AbstractExtension but should never be
+ * called directly with an AbstractRenderer.
+ */
+ @Deprecated
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected Class<Column> getSupportedParentType() {
+ return Column.class;
+ }
+
+ /**
+ * This method is inherited from AbstractExtension but should never be
+ * called directly with an AbstractRenderer.
+ */
+ @Deprecated
+ @Override
+ protected void extend(AbstractClientConnector target) {
+ super.extend(target);
+ }
+
+ @Override
+ public Class<T> getPresentationType() {
+ return presentationType;
+ }
+
+ @Override
+ public JsonValue encode(T value) {
+ 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;
+ }
+
+ /**
+ * Encodes the given value to JSON.
+ * <p>
+ * This is a helper method that can be invoked by an {@link #encode(Object)
+ * encode(T)} override if serializing a value of type other than
+ * {@link #getPresentationType() the presentation type} is desired. For
+ * instance, a {@code Renderer<Date>} could first turn a date value into a
+ * formatted string and return {@code encode(dateString, String.class)}.
+ *
+ * @param value
+ * the value to be encoded
+ * @param type
+ * the type of the value
+ * @return a JSON representation of the given value
+ */
+ protected <U> JsonValue encode(U value, Class<U> type) {
+ return JsonCodec
+ .encode(value, null, type, getUI().getConnectorTracker())
+ .getEncodedValue();
+ }
+} \ No newline at end of file
diff --git a/server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java
new file mode 100644
index 0000000000..dd33ce064c
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import elemental.json.JsonValue;
+
+/**
+ * A renderer for presenting date values.
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public class DateRenderer extends AbstractRenderer<Date> {
+ private final Locale locale;
+ private final String formatString;
+ private final DateFormat dateFormat;
+
+ /**
+ * Creates a new date renderer.
+ * <p>
+ * The renderer is configured to render with the {@link Date#toString()}
+ * representation for the default locale.
+ */
+ public DateRenderer() {
+ this(Locale.getDefault(), "");
+ }
+
+ /**
+ * 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
+ * @throws IllegalArgumentException
+ * if {@code locale} is {@code null}
+ */
+ public DateRenderer(Locale locale) throws IllegalArgumentException {
+ 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);
+ }
+
+ /**
+ * 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
+ * @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) throws IllegalArgumentException {
+ 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);
+ }
+
+ /**
+ * 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
+ * @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)
+ throws IllegalArgumentException {
+ 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");
+ }
+
+ if (locale == null) {
+ throw new IllegalArgumentException("locale may not be null");
+ }
+
+ this.locale = locale;
+ this.formatString = formatString;
+ dateFormat = null;
+ }
+
+ /**
+ * 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) throws IllegalArgumentException {
+ 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");
+ }
+
+ locale = null;
+ formatString = null;
+ this.dateFormat = dateFormat;
+ }
+
+ @Override
+ public String getNullRepresentation() {
+ return super.getNullRepresentation();
+ }
+
+ @Override
+ public JsonValue encode(Date value) {
+ String dateString;
+ if (value == null) {
+ dateString = getNullRepresentation();
+ } else if (dateFormat != null) {
+ dateString = dateFormat.format(value);
+ } else {
+ dateString = String.format(locale, formatString, value);
+ }
+ return encode(dateString, String.class);
+ }
+
+ @Override
+ public String toString() {
+ final String fieldInfo;
+ if (dateFormat != null) {
+ fieldInfo = "dateFormat: " + dateFormat.toString();
+ } else {
+ fieldInfo = "locale: " + locale + ", formatString: " + formatString;
+ }
+
+ return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo);
+ }
+}
diff --git a/server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java
new file mode 100644
index 0000000000..80fcdc18c4
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+/**
+ * A renderer for presenting HTML content.
+ *
+ * @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() {
+ this("");
+ }
+
+ @Override
+ public String getNullRepresentation() {
+ return super.getNullRepresentation();
+ }
+}
diff --git a/server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java
new file mode 100644
index 0000000000..02e6ce68fd
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import elemental.json.JsonValue;
+
+/**
+ * A renderer for presenting number values.
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public class NumberRenderer extends AbstractRenderer<Number> {
+ private final Locale locale;
+ private final NumberFormat numberFormat;
+ private final String formatString;
+
+ /**
+ * Creates a new number renderer.
+ * <p/>
+ * The renderer is configured to render with the number's natural string
+ * representation in the default locale.
+ */
+ public NumberRenderer() {
+ this(Locale.getDefault());
+ }
+
+ /**
+ * 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
+ * @throws IllegalArgumentException
+ * if {@code numberFormat} is {@code null}
+ */
+ 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, nullRepresentation);
+
+ if (numberFormat == null) {
+ throw new IllegalArgumentException("Number format may not be null");
+ }
+
+ locale = null;
+ this.numberFormat = numberFormat;
+ formatString = null;
+ }
+
+ /**
+ * Creates a new number renderer.
+ * <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
+ * if {@code locale} is {@code null}
+ */
+ public NumberRenderer(Locale locale) throws IllegalArgumentException {
+ this("%s", locale);
+ }
+
+ /**
+ * Creates a new number renderer.
+ * <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 href=
+ * "http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax">Format
+ * String Syntax</a>
+ */
+ public NumberRenderer(String formatString) throws IllegalArgumentException {
+ this(formatString, Locale.getDefault(), "");
+ }
+
+ /**
+ * Creates a new number renderer.
+ * <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
+ * the locale in which to present numbers
+ * @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 NumberRenderer(String formatString, Locale locale,
+ String nullRepresentation) {
+ super(Number.class, nullRepresentation);
+
+ if (formatString == null) {
+ throw new IllegalArgumentException("Format string may not be null");
+ }
+
+ if (locale == null) {
+ throw new IllegalArgumentException("Locale may not be null");
+ }
+
+ this.locale = locale;
+ numberFormat = null;
+ this.formatString = formatString;
+ }
+
+ @Override
+ public JsonValue encode(Number value) {
+ String stringValue;
+ 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);
+ } else {
+ throw new IllegalStateException(String.format(
+ "Internal bug: " + "%s is in an illegal state: "
+ + "[locale: %s, numberFormat: %s, formatString: %s]",
+ getClass().getSimpleName(), locale, numberFormat,
+ formatString));
+ }
+ return encode(stringValue, String.class);
+ }
+
+ @Override
+ public String toString() {
+ final String fieldInfo;
+ if (numberFormat != null) {
+ fieldInfo = "numberFormat: " + numberFormat.toString();
+ } else {
+ fieldInfo = "locale: " + locale + ", formatString: " + formatString;
+ }
+
+ return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo);
+ }
+
+ @Override
+ public String getNullRepresentation() {
+ return super.getNullRepresentation();
+ }
+}
diff --git a/server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java
new file mode 100644
index 0000000000..7d05959484
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+import elemental.json.JsonValue;
+
+/**
+ * A renderer that represents double values between 0 and 1 as a graphical
+ * progress bar.
+ *
+ * @author Vaadin Ltd
+ * @since 7.4
+ */
+public class ProgressBarRenderer extends AbstractRenderer<Double> {
+
+ /**
+ * Creates a new text renderer
+ */
+ public ProgressBarRenderer() {
+ 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/main/java/com/vaadin/ui/renderers/Renderer.java b/server/src/main/java/com/vaadin/ui/renderers/Renderer.java
new file mode 100644
index 0000000000..28582d4e8d
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/Renderer.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.Extension;
+
+import elemental.json.JsonValue;
+
+/**
+ * A ClientConnector for controlling client-side
+ * {@link com.vaadin.client.renderers.Renderer Grid renderers}. Renderers
+ * currently extend the Extension interface, 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
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public interface Renderer<T> extends Extension {
+
+ /**
+ * Returns the class literal corresponding to the presentation type T.
+ *
+ * @return the class literal of T
+ */
+ Class<T> getPresentationType();
+
+ /**
+ * Encodes the given value into a {@link JsonValue}.
+ *
+ * @param value
+ * the value to encode
+ * @return a JSON representation of the given value
+ */
+ JsonValue encode(T value);
+
+ /**
+ * This method is inherited from Extension but should never be called
+ * directly with a Renderer.
+ */
+ @Override
+ @Deprecated
+ void remove();
+
+ /**
+ * This method is inherited from Extension but should never be called
+ * directly with a Renderer.
+ */
+ @Override
+ @Deprecated
+ void setParent(ClientConnector parent);
+}
diff --git a/server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java
new file mode 100644
index 0000000000..07f09b88f2
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2016 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.renderers;
+
+/**
+ * A renderer for presenting simple plain-text string values.
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public class TextRenderer extends AbstractRenderer<String> {
+
+ /**
+ * Creates a new text renderer
+ */
+ public TextRenderer() {
+ 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();
+ }
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java
index c426ffe4f6..3dc6dad770 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java
@@ -17,7 +17,7 @@ public class GridTest {
@Before
public void setUp() {
grid = new Grid<>();
- grid.addColumn("foo", String.class, Function.identity());
+ grid.addColumn("foo", Function.identity());
}
@Test