diff options
Diffstat (limited to 'shared')
15 files changed, 1526 insertions, 0 deletions
diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java new file mode 100644 index 0000000000..43469914e5 --- /dev/null +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-2013 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.shared.data; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * RPC interface used for pushing container data to the client. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public interface DataProviderRpc extends ClientRpc { + + /** + * Sends updated row data to a client. + * <p> + * rowDataJson represents a JSON array of JSON objects in the following + * format: + * + * <pre> + * [{ + * "d": [COL_1_JSON, COL_2_json, ...], + * "k": "1" + * }, + * ... + * ] + * </pre> + * + * where COL_INDEX is the index of the column (as a string), and COL_n_JSON + * is valid JSON of the column's data. + * + * @param firstRowIndex + * the index of the first updated row + * @param rowDataJson + * the updated row data + * @see com.vaadin.shared.ui.grid.GridState#JSONKEY_DATA + * @see com.vaadin.ui.components.grid.Renderer#encode(Object) + */ + public void setRowData(int firstRowIndex, String rowDataJson); + + /** + * Informs the client to remove row data. + * + * @param firstRowIndex + * the index of the first removed row + * @param count + * the number of rows removed from <code>firstRowIndex</code> and + * onwards + */ + public void removeRowData(int firstRowIndex, int count); + + /** + * Informs the client to insert new row data. + * + * @param firstRowIndex + * the index of the first new row + * @param count + * the number of rows inserted at <code>firstRowIndex</code> + */ + public void insertRowData(int firstRowIndex, int count); +} diff --git a/shared/src/com/vaadin/shared/data/DataProviderState.java b/shared/src/com/vaadin/shared/data/DataProviderState.java new file mode 100644 index 0000000000..2eabe0b0e1 --- /dev/null +++ b/shared/src/com/vaadin/shared/data/DataProviderState.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2013 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.shared.data; + +import com.vaadin.shared.communication.SharedState; + +/** + * Shared state used by client-side data sources. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class DataProviderState extends SharedState { + /** + * The size of the container. + */ + public int containerSize; +} diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java new file mode 100644 index 0000000000..b2a3e6d2ba --- /dev/null +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2013 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.shared.data; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * RPC interface used for requesting container data to the client. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public interface DataRequestRpc extends ServerRpc { + + /** + * Request rows from the server. + * + * @param firstRowIndex + * the index of the first requested row + * @param numberOfRows + * the number of requested rows + * @param firstCachedRowIndex + * the index of the first cached row + * @param cacheSize + * the number of cached rows + */ + public void requestRows(int firstRowIndex, int numberOfRows, + int firstCachedRowIndex, int cacheSize); +} diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java new file mode 100644 index 0000000000..a8e0f87457 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * The column group row data shared between the server and client + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class ColumnGroupRowState implements Serializable { + + /** + * The groups that has been added to the row + */ + public List<ColumnGroupState> groups = new ArrayList<ColumnGroupState>(); + + /** + * Is the header shown + */ + public boolean headerVisible = true; + + /** + * Is the footer shown + */ + public boolean footerVisible = false; + +} diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java new file mode 100644 index 0000000000..3992b6611f --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * The column group data shared between the server and the client + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class ColumnGroupState implements Serializable { + + /** + * The columns that is included in the group + */ + public List<String> columns = new ArrayList<String>(); + + /** + * The header text of the group + */ + public String header; + + /** + * The footer text of the group + */ + public String footer; +} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java new file mode 100644 index 0000000000..00cc93d371 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * Server-to-client RPC interface for the Grid component. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public interface GridClientRpc extends ClientRpc { + + /** + * Command client Grid to scroll to a specific data row. + * + * @param row + * zero-based row index. If the row index is below zero or above + * the row count of the client-side data source, a client-side + * exception will be triggered. Since this exception has no + * handling by default, an out-of-bounds value will cause a + * client-side crash. + * @param destination + * desired placement of scrolled-to row. See the documentation + * for {@link ScrollDestination} for more information. + */ + public void scrollToRow(int row, ScrollDestination destination); + + /** + * Command client Grid to scroll to the first row. + */ + public void scrollToStart(); + + /** + * Command client Grid to scroll to the last row. + */ + public void scrollToEnd(); + +} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java new file mode 100644 index 0000000000..d1df08c294 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import java.io.Serializable; + +import com.vaadin.shared.Connector; + +/** + * Column state DTO for transferring column properties from the server to the + * client + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class GridColumnState implements Serializable { + + /** + * Id used by grid connector to map server side column with client side + * column + */ + public String id; + + /** + * Header caption for the column + */ + public String header; + + /** + * Footer caption for the column + */ + public String footer; + + /** + * Has the column been hidden. By default the column is visible. + */ + public boolean visible = true; + + /** + * Column width in pixels. Default column width is 100px. + */ + public int width = 100; + + public Connector rendererConnector; + + /** + * Are sorting indicators shown for a column. Default is false. + */ + public boolean sortable = false; +} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java new file mode 100644 index 0000000000..8b264bf426 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import java.io.Serializable; + +/** + * Container class for common constants and default values used by the Grid + * component. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public final class GridConstants implements Serializable { + + /** + * Default padding in pixels when scrolling programmatically, without an + * explicitly defined padding value. + */ + public static final int DEFAULT_PADDING = 0; +} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java new file mode 100644 index 0000000000..b763174e53 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.grid; + +import java.util.List; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * Client-to-server RPC interface for the Grid component + * + * @since 7.4 + * @author Vaadin Ltd + */ +public interface GridServerRpc extends ServerRpc { + void selectionChange(List<String> newSelection); +} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java new file mode 100644 index 0000000000..3dcf7764ac --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -0,0 +1,135 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.shared.AbstractComponentState; +import com.vaadin.shared.annotations.DelegateToWidget; + +/** + * The shared state for the {@link com.vaadin.ui.components.grid.Grid} component + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class GridState extends AbstractComponentState { + + /** + * A description of which of the three bundled SelectionModels is currently + * in use. + * <p> + * Used as a data transfer object instead of the client/server ones, because + * they don't know about each others classes. + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode + * @see com.vaadin.client.ui.grid.Grid.SelectionMode + */ + public enum SharedSelectionMode { + /** + * Representation of a single selection mode + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode#SINGLE + * @see com.vaadin.client.ui.grid.Grid.SelectionMode#SINGLE + */ + SINGLE, + + /** + * Representation of a multiselection mode + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode#MULTI + * @see com.vaadin.client.ui.grid.Grid.SelectionMode#MULTI + */ + MULTI, + + /** + * Representation of a no-selection mode + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode#NONE + * @see com.vaadin.client.ui.grid.Grid.SelectionMode#NONE + */ + NONE; + } + + /** + * The default value for height-by-rows for both GWT widgets + * {@link com.vaadin.ui.components.grid Grid} and + * {@link com.vaadin.client.ui.grid.Escalator Escalator} + */ + public static final double DEFAULT_HEIGHT_BY_ROWS = 10.0d; + + /** + * The key in which a row's data can be found + * + * @see com.vaadin.shared.data.DataProviderRpc#setRowData(int, String) + */ + public static final String JSONKEY_DATA = "d"; + + /** + * The key in which a row's own key can be found + * + * @see com.vaadin.shared.data.DataProviderRpc#setRowData(int, String) + */ + public static final String JSONKEY_ROWKEY = "k"; + + { + // FIXME Grid currently does not support undefined size + width = "400px"; + height = "400px"; + } + + /** + * Columns in grid. Column order implicitly deferred from list order. + */ + public List<GridColumnState> columns = new ArrayList<GridColumnState>(); + + /** + * Is the column header row visible + */ + public boolean columnHeadersVisible = true; + + /** + * Is the column footer row visible + */ + public boolean columnFootersVisible = false; + + /** + * The column groups added to the grid + */ + public List<ColumnGroupRowState> columnGroupRows = new ArrayList<ColumnGroupRowState>(); + + /** + * The id for the last frozen column. + * + * @see GridColumnState#id + */ + public String lastFrozenColumnId = null; + + /** The height of the Grid in terms of body rows. */ + @DelegateToWidget + public double heightByRows = DEFAULT_HEIGHT_BY_ROWS; + + /** The mode by which Grid defines its height. */ + @DelegateToWidget + public HeightMode heightMode = HeightMode.CSS; + + // instantiated just to avoid NPEs + public List<String> selectedKeys = new ArrayList<String>(); + + public SharedSelectionMode selectionMode; +} diff --git a/shared/src/com/vaadin/shared/ui/grid/HeightMode.java b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java new file mode 100644 index 0000000000..0146e53e73 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +/** + * The modes for height calculation that are supported by Grid ( + * {@link com.vaadin.client.ui.grid.Grid client} and + * {@link com.vaadin.ui.components.grid.Grid server}) / + * {@link com.vaadin.client.ui.grid.Escalator Escalator}. + * + * @since 7.2 + * @author Vaadin Ltd + * @see com.vaadin.client.ui.grid.Grid#setHeightMode(HeightMode) + * @see com.vaadin.ui.components.grid.Grid#setHeightMode(HeightMode) + * @see com.vaadin.client.ui.grid.Escalator#setHeightMode(HeightMode) + */ +public enum HeightMode { + /** + * The height of the Component or Widget is defined by a CSS-like value. + * (e.g. "100px", "50em" or "25%") + */ + CSS, + + /** + * The height of the Component or Widget in question is defined by a number + * of rows. + */ + ROW; +} diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java new file mode 100644 index 0000000000..c28502256c --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -0,0 +1,431 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import java.io.Serializable; + +/** + * An immutable representation of a range, marked by start and end points. + * <p> + * The range is treated as inclusive at the start, and exclusive at the end. + * I.e. the range [0..1[ has the length 1, and represents one integer: 0. + * <p> + * The range is considered {@link #isEmpty() empty} if the start is the same as + * the end. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public final class Range implements Serializable { + private final int start; + private final int end; + + /** + * Creates a range object representing a single integer. + * + * @param integer + * the number to represent as a range + * @return the range represented by <code>integer</code> + */ + public static Range withOnly(final int integer) { + return new Range(integer, integer + 1); + } + + /** + * Creates a range between two integers. + * <p> + * The range start is <em>inclusive</em> and the end is <em>exclusive</em>. + * So, a range "between" 0 and 5 represents the numbers 0, 1, 2, 3 and 4, + * but not 5. + * + * @param start + * the start of the the range, inclusive + * @param end + * the end of the range, exclusive + * @return a range representing <code>[start..end[</code> + * @throws IllegalArgumentException + * if <code>start > end</code> + */ + public static Range between(final int start, final int end) + throws IllegalArgumentException { + return new Range(start, end); + } + + /** + * Creates a range from a start point, with a given length. + * + * @param start + * the first integer to include in the range + * @param length + * the length of the resulting range + * @return a range starting from <code>start</code>, with + * <code>length</code> number of integers following + * @throws IllegalArgumentException + * if length < 0 + */ + public static Range withLength(final int start, final int length) + throws IllegalArgumentException { + if (length < 0) { + /* + * The constructor of Range will throw an exception if start > + * start+length (i.e. if length is negative). We're throwing the + * same exception type, just with a more descriptive message. + */ + throw new IllegalArgumentException("length must not be negative"); + } + return new Range(start, start + length); + } + + /** + * Creates a new range between two numbers: <code>[start..end[</code>. + * + * @param start + * the start integer, inclusive + * @param end + * the end integer, exclusive + * @throws IllegalArgumentException + * if <code>start > end</code> + */ + private Range(final int start, final int end) + throws IllegalArgumentException { + if (start > end) { + throw new IllegalArgumentException( + "start must not be greater than end"); + } + + this.start = start; + this.end = end; + } + + /** + * Returns the <em>inclusive</em> start point of this range. + * + * @return the start point of this range + */ + public int getStart() { + return start; + } + + /** + * Returns the <em>exclusive</em> end point of this range. + * + * @return the end point of this range + */ + public int getEnd() { + return end; + } + + /** + * The number of integers contained in the range. + * + * @return the number of integers contained in the range + */ + public int length() { + return getEnd() - getStart(); + } + + /** + * Checks whether the range has no elements between the start and end. + * + * @return <code>true</code> iff the range contains no elements. + */ + public boolean isEmpty() { + return getStart() >= getEnd(); + } + + /** + * Checks whether this range and another range are at least partially + * covering the same values. + * + * @param other + * the other range to check against + * @return <code>true</code> if this and <code>other</code> intersect + */ + public boolean intersects(final Range other) { + return getStart() < other.getEnd() && other.getStart() < getEnd(); + } + + /** + * Checks whether an integer is found within this range. + * + * @param integer + * an integer to test for presence in this range + * @return <code>true</code> iff <code>integer</code> is in this range + */ + public boolean contains(final int integer) { + return getStart() <= integer && integer < getEnd(); + } + + /** + * Checks whether this range is a subset of another range. + * + * @return <code>true</code> iff <code>other</code> completely wraps this + * range + */ + public boolean isSubsetOf(final Range other) { + return other.getStart() <= getStart() && getEnd() <= other.getEnd(); + } + + /** + * Overlay this range with another one, and partition the ranges according + * to how they position relative to each other. + * <p> + * The three partitions are returned as a three-element Range array: + * <ul> + * <li>Elements in this range that occur before elements in + * <code>other</code>. + * <li>Elements that are shared between the two ranges. + * <li>Elements in this range that occur after elements in + * <code>other</code>. + * </ul> + * + * @param other + * the other range to act as delimiters. + * @return a three-element Range array of partitions depicting the elements + * before (index 0), shared/inside (index 1) and after (index 2). + */ + public Range[] partitionWith(final Range other) { + final Range[] splitBefore = splitAt(other.getStart()); + final Range rangeBefore = splitBefore[0]; + final Range[] splitAfter = splitBefore[1].splitAt(other.getEnd()); + final Range rangeInside = splitAfter[0]; + final Range rangeAfter = splitAfter[1]; + return new Range[] { rangeBefore, rangeInside, rangeAfter }; + } + + /** + * Get a range that is based on this one, but offset by a number. + * + * @param offset + * the number to offset by + * @return a copy of this range, offset by <code>offset</code> + */ + public Range offsetBy(final int offset) { + if (offset == 0) { + return this; + } else { + return new Range(start + offset, end + offset); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [" + getStart() + ".." + getEnd() + + "[" + (isEmpty() ? " (empty)" : ""); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + end; + result = prime * result + start; + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Range other = (Range) obj; + if (end != other.end) { + return false; + } + if (start != other.start) { + return false; + } + return true; + } + + /** + * Checks whether this range starts before the start of another range. + * + * @param other + * the other range to compare against + * @return <code>true</code> iff this range starts before the + * <code>other</code> + */ + public boolean startsBefore(final Range other) { + return getStart() < other.getStart(); + } + + /** + * Checks whether this range ends before the start of another range. + * + * @param other + * the other range to compare against + * @return <code>true</code> iff this range ends before the + * <code>other</code> + */ + public boolean endsBefore(final Range other) { + return getEnd() <= other.getStart(); + } + + /** + * Checks whether this range ends after the end of another range. + * + * @param other + * the other range to compare against + * @return <code>true</code> iff this range ends after the + * <code>other</code> + */ + public boolean endsAfter(final Range other) { + return getEnd() > other.getEnd(); + } + + /** + * Checks whether this range starts after the end of another range. + * + * @param other + * the other range to compare against + * @return <code>true</code> iff this range starts after the + * <code>other</code> + */ + public boolean startsAfter(final Range other) { + return getStart() >= other.getEnd(); + } + + /** + * Split the range into two at a certain integer. + * <p> + * <em>Example:</em> <code>[5..10[.splitAt(7) == [5..7[, [7..10[</code> + * + * @param integer + * the integer at which to split the range into two + * @return an array of two ranges, with <code>[start..integer[</code> in the + * first element, and <code>[integer..end[</code> in the second + * element. + * <p> + * If {@code integer} is less than {@code start}, [empty, + * {@code this} ] is returned. if <code>integer</code> is equal to + * or greater than {@code end}, [{@code this}, empty] is returned + * instead. + */ + public Range[] splitAt(final int integer) { + if (integer < start) { + return new Range[] { Range.withLength(start, 0), this }; + } else if (integer >= end) { + return new Range[] { this, Range.withLength(end, 0) }; + } else { + return new Range[] { new Range(start, integer), + new Range(integer, end) }; + } + } + + /** + * Split the range into two after a certain number of integers into the + * range. + * <p> + * Calling this method is equivalent to calling + * <code>{@link #splitAt(int) splitAt}({@link #getStart()}+length);</code> + * <p> + * <em>Example:</em> + * <code>[5..10[.splitAtFromStart(2) == [5..7[, [7..10[</code> + * + * @param length + * the length at which to split this range into two + * @return an array of two ranges, having the <code>length</code>-first + * elements of this range, and the second range having the rest. If + * <code>length</code> ≤ 0, the first element will be empty, and + * the second element will be this range. If <code>length</code> + * ≥ {@link #length()}, the first element will be this range, + * and the second element will be empty. + */ + public Range[] splitAtFromStart(final int length) { + return splitAt(getStart() + length); + } + + /** + * Combines two ranges to create a range containing all values in both + * ranges, provided there are no gaps between the ranges. + * + * @param other + * the range to combine with this range + * + * @return the combined range + * + * @throws IllegalArgumentException + * if the two ranges aren't connected + */ + public Range combineWith(Range other) throws IllegalArgumentException { + if (getStart() > other.getEnd() || other.getStart() > getEnd()) { + throw new IllegalArgumentException("There is a gap between " + this + + " and " + other); + } + + return Range.between(Math.min(getStart(), other.getStart()), + Math.max(getEnd(), other.getEnd())); + } + + /** + * Creates a range that is expanded the given amounts in both ends. + * + * @param startDelta + * the amount to expand by in the beginning of the range + * @param endDelta + * the amount to expand by in the end of the range + * + * @return an expanded range + * + * @throws IllegalArgumentException + * if the new range would have <code>start > end</code> + */ + public Range expand(int startDelta, int endDelta) + throws IllegalArgumentException { + return Range.between(getStart() - startDelta, getEnd() + endDelta); + } + + /** + * Limits this range to be within the bounds of the provided range. + * <p> + * This is basically an optimized way of calculating + * <code>{@link #partitionWith(Range)}[1]</code> without the overhead of + * defining the parts that do not overlap. + * <p> + * If the two ranges do not intersect, an empty range is returned. There are + * no guarantees about the position of that range. + * + * @param bounds + * the bounds that the returned range should be limited to + * @return a bounded range + */ + public Range restrictTo(Range bounds) { + boolean startWithin = getStart() >= bounds.getStart(); + boolean endWithin = getEnd() <= bounds.getEnd(); + + if (startWithin) { + if (endWithin) { + return this; + } else { + return Range.between(getStart(), bounds.getEnd()); + } + } else { + if (endWithin) { + return Range.between(bounds.getStart(), getEnd()); + } else { + return bounds; + } + } + } +} diff --git a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java new file mode 100644 index 0000000000..decc2fab5f --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +/** + * Enumeration, specifying the destinations that are supported when scrolling + * rows or columns into view. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public enum ScrollDestination { + + /** + * Scroll as little as possible to show the target element. If the element + * fits into view, this works as START or END depending on the current + * scroll position. If the element does not fit into view, this works as + * START. + */ + ANY, + + /** + * Scrolls so that the element is shown at the start of the viewport. The + * viewport will, however, not scroll beyond its contents. + */ + START, + + /** + * Scrolls so that the element is shown in the middle of the viewport. The + * viewport will, however, not scroll beyond its contents, given more + * elements than what the viewport is able to show at once. Under no + * circumstances will the viewport scroll before its first element. + */ + MIDDLE, + + /** + * Scrolls so that the element is shown at the end of the viewport. The + * viewport will, however, not scroll before its first element. + */ + END + +} diff --git a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java new file mode 100644 index 0000000000..3a1828992e --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.grid; + +/** + * Describes sorting direction for a Grid column + * + * @since 7.4 + * @author Vaadin Ltd + */ +public enum SortDirection { + + /** + * Ascending (e.g. A-Z, 1..9) sort order + */ + ASCENDING, + + /** + * Descending (e.g. Z-A, 9..1) sort order + */ + DESCENDING +} diff --git a/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java b/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java new file mode 100644 index 0000000000..ab67b22d0b --- /dev/null +++ b/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java @@ -0,0 +1,406 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +@SuppressWarnings("static-method") +public class RangeTest { + + @Test(expected = IllegalArgumentException.class) + public void startAfterEndTest() { + Range.between(10, 9); + } + + @Test(expected = IllegalArgumentException.class) + public void negativeLengthTest() { + Range.withLength(10, -1); + } + + @Test + public void constructorEquivalenceTest() { + assertEquals("10 == [10,11[", Range.withOnly(10), Range.between(10, 11)); + assertEquals("[10,20[ == 10, length 10", Range.between(10, 20), + Range.withLength(10, 10)); + assertEquals("10 == 10, length 1", Range.withOnly(10), + Range.withLength(10, 1)); + } + + @Test + public void boundsTest() { + { + final Range range = Range.between(0, 10); + assertEquals("between(0, 10) start", 0, range.getStart()); + assertEquals("between(0, 10) end", 10, range.getEnd()); + } + + { + final Range single = Range.withOnly(10); + assertEquals("withOnly(10) start", 10, single.getStart()); + assertEquals("withOnly(10) end", 11, single.getEnd()); + } + + { + final Range length = Range.withLength(10, 5); + assertEquals("withLength(10, 5) start", 10, length.getStart()); + assertEquals("withLength(10, 5) end", 15, length.getEnd()); + } + } + + @Test + @SuppressWarnings("boxing") + public void equalsTest() { + final Range range1 = Range.between(0, 10); + final Range range2 = Range.withLength(0, 11); + + assertTrue("null", !range1.equals(null)); + assertTrue("reflexive", range1.equals(range1)); + assertEquals("symmetric", range1.equals(range2), range2.equals(range1)); + } + + @Test + public void containsTest() { + final int start = 0; + final int end = 10; + final Range range = Range.between(start, end); + + assertTrue("start should be contained", range.contains(start)); + assertTrue("start-1 should not be contained", + !range.contains(start - 1)); + assertTrue("end should not be contained", !range.contains(end)); + assertTrue("end-1 should be contained", range.contains(end - 1)); + + assertTrue("[0..10[ contains 5", Range.between(0, 10).contains(5)); + assertTrue("empty range does not contain 5", !Range.between(5, 5) + .contains(5)); + } + + @Test + public void emptyTest() { + assertTrue("[0..0[ should be empty", Range.between(0, 0).isEmpty()); + assertTrue("Range of length 0 should be empty", Range.withLength(0, 0) + .isEmpty()); + + assertTrue("[0..1[ should not be empty", !Range.between(0, 1).isEmpty()); + assertTrue("Range of length 1 should not be empty", + !Range.withLength(0, 1).isEmpty()); + } + + @Test + public void splitTest() { + final Range startRange = Range.between(0, 10); + final Range[] splitRanges = startRange.splitAt(5); + assertEquals("[0..10[ split at 5, lower", Range.between(0, 5), + splitRanges[0]); + assertEquals("[0..10[ split at 5, upper", Range.between(5, 10), + splitRanges[1]); + } + + @Test + public void split_valueBefore() { + Range range = Range.between(10, 20); + Range[] splitRanges = range.splitAt(5); + + assertEquals(Range.between(10, 10), splitRanges[0]); + assertEquals(range, splitRanges[1]); + } + + @Test + public void split_valueAfter() { + Range range = Range.between(10, 20); + Range[] splitRanges = range.splitAt(25); + + assertEquals(range, splitRanges[0]); + assertEquals(Range.between(20, 20), splitRanges[1]); + } + + @Test + public void emptySplitTest() { + final Range range = Range.between(5, 10); + final Range[] split1 = range.splitAt(0); + assertTrue("split1, [0]", split1[0].isEmpty()); + assertEquals("split1, [1]", range, split1[1]); + + final Range[] split2 = range.splitAt(15); + assertEquals("split2, [0]", range, split2[0]); + assertTrue("split2, [1]", split2[1].isEmpty()); + } + + @Test + public void lengthTest() { + assertEquals("withLength length", 5, Range.withLength(10, 5).length()); + assertEquals("between length", 5, Range.between(10, 15).length()); + assertEquals("withOnly 10 length", 1, Range.withOnly(10).length()); + } + + @Test + public void intersectsTest() { + assertTrue("[0..10[ intersects [5..15[", Range.between(0, 10) + .intersects(Range.between(5, 15))); + assertTrue("[0..10[ does not intersect [10..20[", !Range.between(0, 10) + .intersects(Range.between(10, 20))); + } + + @Test + public void intersects_emptyInside() { + assertTrue("[5..5[ does intersect with [0..10[", Range.between(5, 5) + .intersects(Range.between(0, 10))); + assertTrue("[0..10[ does intersect with [5..5[", Range.between(0, 10) + .intersects(Range.between(5, 5))); + } + + @Test + public void intersects_emptyOutside() { + assertTrue("[15..15[ does not intersect with [0..10[", + !Range.between(15, 15).intersects(Range.between(0, 10))); + assertTrue("[0..10[ does not intersect with [15..15[", + !Range.between(0, 10).intersects(Range.between(15, 15))); + } + + @Test + public void subsetTest() { + assertTrue("[5..10[ is subset of [0..20[", Range.between(5, 10) + .isSubsetOf(Range.between(0, 20))); + + final Range range = Range.between(0, 10); + assertTrue("range is subset of self", range.isSubsetOf(range)); + + assertTrue("[0..10[ is not subset of [5..15[", !Range.between(0, 10) + .isSubsetOf(Range.between(5, 15))); + } + + @Test + public void offsetTest() { + assertEquals(Range.between(5, 15), Range.between(0, 10).offsetBy(5)); + } + + @Test + public void rangeStartsBeforeTest() { + final Range former = Range.between(0, 5); + final Range latter = Range.between(1, 5); + assertTrue("former should starts before latter", + former.startsBefore(latter)); + assertTrue("latter shouldn't start before latter", + !latter.startsBefore(former)); + + assertTrue("no overlap allowed", + !Range.between(0, 5).startsBefore(Range.between(0, 10))); + } + + @Test + public void rangeStartsAfterTest() { + final Range former = Range.between(0, 5); + final Range latter = Range.between(5, 10); + assertTrue("latter should start after former", + latter.startsAfter(former)); + assertTrue("former shouldn't start after latter", + !former.startsAfter(latter)); + + assertTrue("no overlap allowed", + !Range.between(5, 10).startsAfter(Range.between(0, 6))); + } + + @Test + public void rangeEndsBeforeTest() { + final Range former = Range.between(0, 5); + final Range latter = Range.between(5, 10); + assertTrue("latter should end before former", former.endsBefore(latter)); + assertTrue("former shouldn't end before latter", + !latter.endsBefore(former)); + + assertTrue("no overlap allowed", + !Range.between(5, 10).endsBefore(Range.between(9, 15))); + } + + @Test + public void rangeEndsAfterTest() { + final Range former = Range.between(1, 5); + final Range latter = Range.between(1, 6); + assertTrue("latter should end after former", latter.endsAfter(former)); + assertTrue("former shouldn't end after latter", + !former.endsAfter(latter)); + + assertTrue("no overlap allowed", + !Range.between(0, 10).endsAfter(Range.between(5, 10))); + } + + @Test(expected = IllegalArgumentException.class) + public void combine_notOverlappingFirstSmaller() { + Range.between(0, 10).combineWith(Range.between(11, 20)); + } + + @Test(expected = IllegalArgumentException.class) + public void combine_notOverlappingSecondLarger() { + Range.between(11, 20).combineWith(Range.between(0, 10)); + } + + @Test(expected = IllegalArgumentException.class) + public void combine_firstEmptyNotOverlapping() { + Range.between(15, 15).combineWith(Range.between(0, 10)); + } + + @Test(expected = IllegalArgumentException.class) + public void combine_secondEmptyNotOverlapping() { + Range.between(0, 10).combineWith(Range.between(15, 15)); + } + + @Test + public void combine_barelyOverlapping() { + Range r1 = Range.between(0, 10); + Range r2 = Range.between(10, 20); + + // Test both ways, should give the same result + Range combined1 = r1.combineWith(r2); + Range combined2 = r2.combineWith(r1); + assertEquals(combined1, combined2); + + assertEquals(0, combined1.getStart()); + assertEquals(20, combined1.getEnd()); + } + + @Test + public void combine_subRange() { + Range r1 = Range.between(0, 10); + Range r2 = Range.between(2, 8); + + // Test both ways, should give the same result + Range combined1 = r1.combineWith(r2); + Range combined2 = r2.combineWith(r1); + assertEquals(combined1, combined2); + + assertEquals(r1, combined1); + } + + @Test + public void combine_intersecting() { + Range r1 = Range.between(0, 10); + Range r2 = Range.between(5, 15); + + // Test both ways, should give the same result + Range combined1 = r1.combineWith(r2); + Range combined2 = r2.combineWith(r1); + assertEquals(combined1, combined2); + + assertEquals(0, combined1.getStart()); + assertEquals(15, combined1.getEnd()); + + } + + @Test + public void combine_emptyInside() { + Range r1 = Range.between(0, 10); + Range r2 = Range.between(5, 5); + + // Test both ways, should give the same result + Range combined1 = r1.combineWith(r2); + Range combined2 = r2.combineWith(r1); + assertEquals(combined1, combined2); + + assertEquals(r1, combined1); + } + + @Test + public void expand_basic() { + Range r1 = Range.between(5, 10); + Range r2 = r1.expand(2, 3); + + assertEquals(Range.between(3, 13), r2); + } + + @Test + public void expand_negativeLegal() { + Range r1 = Range.between(5, 10); + + Range r2 = r1.expand(-2, -2); + assertEquals(Range.between(7, 8), r2); + + Range r3 = r1.expand(-3, -2); + assertEquals(Range.between(8, 8), r3); + + Range r4 = r1.expand(3, -8); + assertEquals(Range.between(2, 2), r4); + } + + @Test(expected = IllegalArgumentException.class) + public void expand_negativeIllegal1() { + Range r1 = Range.between(5, 10); + + // Should throw because the start would contract beyond the end + r1.expand(-3, -3); + + } + + @Test(expected = IllegalArgumentException.class) + public void expand_negativeIllegal2() { + Range r1 = Range.between(5, 10); + + // Should throw because the end would contract beyond the start + r1.expand(3, -9); + } + + @Test + public void restrictTo_fullyInside() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(4, 11); + + Range r3 = r1.restrictTo(r2); + assertTrue(r1 == r3); + } + + @Test + public void restrictTo_fullyOutside() { + Range r1 = Range.between(4, 11); + Range r2 = Range.between(5, 10); + + Range r3 = r1.restrictTo(r2); + assertTrue(r2 == r3); + } + + public void restrictTo_notInterstecting() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(15, 20); + + Range r3 = r1.restrictTo(r2); + assertTrue("Non-intersecting ranges should produce an empty result", + r3.isEmpty()); + + Range r4 = r2.restrictTo(r1); + assertTrue("Non-intersecting ranges should produce an empty result", + r4.isEmpty()); + } + + public void restrictTo_startOutside() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(7, 15); + + Range r3 = r1.restrictTo(r2); + + assertEquals(Range.between(7, 10), r3); + } + + public void restrictTo_endOutside() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(4, 7); + + Range r3 = r1.restrictTo(r2); + + assertEquals(Range.between(5, 7), r3); + } + +} |