diff options
author | Adam Wagner <wbadam@users.noreply.github.com> | 2017-07-03 13:36:17 +0200 |
---|---|---|
committer | Henri Sara <henri.sara@gmail.com> | 2017-07-03 14:36:17 +0300 |
commit | 6ecef502864ed201b468a68c00676beb401f21c7 (patch) | |
tree | 2f96341ebcb02bced9092e4d3196266ee2eb2740 | |
parent | 4d085fd8b3589db4fbbdcd52643cee4b20f70e31 (diff) | |
download | vaadin-framework-6ecef502864ed201b468a68c00676beb401f21c7.tar.gz vaadin-framework-6ecef502864ed201b468a68c00676beb401f21c7.zip |
Add criteria API to make it easier to set acceptance criteria for drag and drop (#9605)
Fixes #9600
11 files changed, 689 insertions, 38 deletions
diff --git a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java index 313c990074..b91b0aace0 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java @@ -412,6 +412,11 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { for (String type : getState().types) { orderedData.put(type, getState().data.get(type)); } + + // Add payload for comparing against acceptance criteria + getState().payload.values().forEach(payload -> orderedData + .put(payload.getPayloadString(), payload.getValue())); + return orderedData; } diff --git a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java index 7e64b0d305..3791db53b4 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java @@ -17,8 +17,10 @@ package com.vaadin.client.extensions; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.DataTransfer; @@ -34,6 +36,7 @@ import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.dnd.DropEffect; import com.vaadin.shared.ui.dnd.DropTargetRpc; import com.vaadin.shared.ui.dnd.DropTargetState; +import com.vaadin.shared.ui.dnd.criteria.Payload; import com.vaadin.ui.dnd.DropTargetExtension; import elemental.events.Event; @@ -320,12 +323,43 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { // Currently Safari, Edge and IE don't follow the spec by allowing drop // if those don't match - if (getState().dropCriteria != null) { - return executeScript(event, getState().dropCriteria); + // Allow by default when criteria not set + boolean allowed = true; + + // Execute criteria script + if (getState().criteriaScript != null) { + allowed = executeScript(event, getState().criteriaScript); + } + + // Execute criterion defined via API + if (allowed && getState().criteria != null && !getState().criteria + .isEmpty()) { + + // Collect payload data types + Set<Payload> payloadSet = new HashSet<>(); + JsArrayString typesJsArray = getTypes(event.getDataTransfer()); + for (int i = 0; i < typesJsArray.length(); i++) { + String type = typesJsArray.get(i); + + if (type.startsWith(Payload.ITEM_PREFIX)) { + payloadSet.add(Payload.parse(type)); + } + } + + // Compare payload against criteria + switch (getState().criteriaMatch) { + case ALL: + allowed = getState().criteria.stream() + .allMatch(criterion -> criterion.resolve(payloadSet)); + break; + case ANY: + default: + allowed = getState().criteria.stream() + .anyMatch(criterion -> criterion.resolve(payloadSet)); + } } - // Allow when criteria not set - return true; + return allowed; } /** diff --git a/server/src/main/java/com/vaadin/ui/dnd/DragSourceExtension.java b/server/src/main/java/com/vaadin/ui/dnd/DragSourceExtension.java index 42c29f738a..d123700be9 100644 --- a/server/src/main/java/com/vaadin/ui/dnd/DragSourceExtension.java +++ b/server/src/main/java/com/vaadin/ui/dnd/DragSourceExtension.java @@ -23,10 +23,12 @@ import java.util.Objects; import com.vaadin.server.AbstractExtension; import com.vaadin.server.Resource; import com.vaadin.shared.Registration; +import com.vaadin.shared.ui.dnd.criteria.ComparisonOperator; import com.vaadin.shared.ui.dnd.DragSourceRpc; import com.vaadin.shared.ui.dnd.DragSourceState; import com.vaadin.shared.ui.dnd.DropEffect; import com.vaadin.shared.ui.dnd.EffectAllowed; +import com.vaadin.shared.ui.dnd.criteria.Payload; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.dnd.event.DragEndEvent; import com.vaadin.ui.dnd.event.DragEndListener; @@ -38,7 +40,7 @@ import com.vaadin.ui.dnd.event.DragStartListener; * functionality. * * @param <T> - * Type of the component to be extended. + * Type of the component to be extended. * @author Vaadin Ltd * @since 8.1 */ @@ -58,7 +60,7 @@ public class DragSourceExtension<T extends AbstractComponent> * Extends {@code target} component and makes it a drag source. * * @param target - * Component to be extended. + * Component to be extended. */ public DragSourceExtension(T target) { super.extend(target); @@ -125,7 +127,7 @@ public class DragSourceExtension<T extends AbstractComponent> * side. Fires the {@link DragEndEvent}. * * @param dropEffect - * the drop effect on the dragend + * the drop effect on the dragend */ protected void onDragEnd(DropEffect dropEffect) { DragEndEvent<T> event = new DragEndEvent<>(getParent(), dropEffect); @@ -150,7 +152,7 @@ public class DragSourceExtension<T extends AbstractComponent> * equivalent to {@link EffectAllowed#ALL}. * * @param effect - * Effects to allow for this draggable element. Cannot be {@code + * Effects to allow for this draggable element. Cannot be {@code * null}. */ public void setEffectAllowed(EffectAllowed effect) { @@ -294,6 +296,73 @@ public class DragSourceExtension<T extends AbstractComponent> } /** + * Sets payload for this drag source to use with acceptance criterion. The + * payload is transferred as data type in the data transfer object in the + * following format: {@code "v-item:string:key:value"}. The given value is + * compared to the criterion value when the drag source is dragged on top of + * a drop target that has the suitable criterion. + * <p> + * Note that setting payload in Internet Explorer 11 is not possible due to + * the browser's limitations. + * + * @param key + * key of the payload to be transferred + * @param value + * value of the payload to be transferred + * @see DropTargetExtension#setDropCriterion(String, String) + */ + public void setPayload(String key, String value) { + setPayload(key, String.valueOf(value), Payload.ValueType.STRING); + } + + /** + * Sets payload for this drag source to use with acceptance criterion. The + * payload is transferred as data type in the data transfer object in the + * following format: {@code "v-item:integer:key:value"}. The given value is + * compared to the criterion value when the drag source is dragged on top of + * a drop target that has the suitable criterion. + * <p> + * Note that setting payload in Internet Explorer 11 is not possible due to + * the browser's limitations. + * + * @param key + * key of the payload to be transferred + * @param value + * value of the payload to be transferred + * @see DropTargetExtension#setDropCriterion(String, ComparisonOperator, + * int) + */ + public void setPayload(String key, int value) { + setPayload(key, String.valueOf(value), Payload.ValueType.INTEGER); + } + + /** + * Sets payload for this drag source to use with acceptance criterion. The + * payload is transferred as data type in the data transfer object in the + * following format: {@code "v-item:double:key:value"}. The given value is + * compared to the criterion value when the drag source is dragged on top of + * a drop target that has the suitable criterion. + * <p> + * Note that setting payload in Internet Explorer 11 is not possible due to + * the browser's limitations. + * + * @param key + * key of the payload to be transferred + * @param value + * value of the payload to be transferred + * @see DropTargetExtension#setDropCriterion(String, ComparisonOperator, + * double) + */ + public void setPayload(String key, double value) { + setPayload(key, String.valueOf(value), Payload.ValueType.DOUBLE); + } + + private void setPayload(String key, String value, + Payload.ValueType valueType) { + getState().payload.put(key, new Payload(key, value, valueType)); + } + + /** * Set server side drag data. This data is available in the drop event and * can be used to transfer data between drag source and drop target if they * are in the same UI. @@ -322,7 +391,7 @@ public class DragSourceExtension<T extends AbstractComponent> * dragstart event happens on the client side. * * @param listener - * Listener to handle dragstart event. + * Listener to handle dragstart event. * @return Handle to be used to remove this listener. */ public Registration addDragStartListener(DragStartListener<T> listener) { @@ -337,7 +406,7 @@ public class DragSourceExtension<T extends AbstractComponent> * event happens on the client side. * * @param listener - * Listener to handle dragend event. + * Listener to handle dragend event. * @return Handle to be used to remove this listener. */ public Registration addDragEndListener(DragEndListener<T> listener) { @@ -349,7 +418,7 @@ public class DragSourceExtension<T extends AbstractComponent> * Set a custom drag image for the current drag source. * * @param imageResource - * Resource of the image to be displayed as drag image. + * Resource of the image to be displayed as drag image. */ public void setDragImage(Resource imageResource) { setResource(DragSourceState.RESOURCE_DRAG_IMAGE, imageResource); diff --git a/server/src/main/java/com/vaadin/ui/dnd/DropTargetExtension.java b/server/src/main/java/com/vaadin/ui/dnd/DropTargetExtension.java index 1f5c675b21..4cff2545e8 100644 --- a/server/src/main/java/com/vaadin/ui/dnd/DropTargetExtension.java +++ b/server/src/main/java/com/vaadin/ui/dnd/DropTargetExtension.java @@ -15,6 +15,7 @@ */ package com.vaadin.ui.dnd; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -23,9 +24,11 @@ import java.util.Objects; import com.vaadin.server.AbstractExtension; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; +import com.vaadin.shared.ui.dnd.criteria.ComparisonOperator; import com.vaadin.shared.ui.dnd.DropEffect; import com.vaadin.shared.ui.dnd.DropTargetRpc; import com.vaadin.shared.ui.dnd.DropTargetState; +import com.vaadin.shared.ui.dnd.criteria.Criterion; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.dnd.event.DropEvent; import com.vaadin.ui.dnd.event.DropListener; @@ -146,9 +149,13 @@ public class DropTargetExtension<T extends AbstractComponent> /** * Sets a criteria script in JavaScript to allow drop on this drop target. * The script is executed when something is dragged on top of the target, - * and the drop is not allowed in case the script returns {@code false}. If - * no script is set, then the drop is always accepted, if the set - * {@link #setDropEffect(DropEffect) dropEffect} matches the drag source. + * and the drop is not allowed in case the script returns {@code false}. + * <p> + * Drop will be allowed if it passes both this criteria script and the + * criteria set via any of {@code setDropCriterion()} or {@code + * setDropCriteria()} methods. If no criteria is set, then the drop is + * always accepted, if the set {@link #setDropEffect(DropEffect) dropEffect} + * matches the drag source. * <p> * <b>IMPORTANT:</b> Construct the criteria script carefully and do not * include untrusted sources such as user input. Always keep in mind that @@ -157,22 +164,23 @@ public class DropTargetExtension<T extends AbstractComponent> * Example: * * <pre> - * target.setDropCriteria( - * // If dragged source contains a URL, allow it to be dropped - * "if (event.dataTransfer.types.includes('text/uri-list')) {" - * + " return true;" + "}" + + * target.setDropCriterion( + * // If dragged source contains a URL, allow it to be dropped + * "if (event.dataTransfer.types.includes('text/uri-list')) {" + + * " return true;" + + * "}" + * - * // Otherwise cancel the event" - * "return false;"); + * // Otherwise cancel the event + * "return false;"); * </pre> * * @param criteriaScript * JavaScript to be executed when drop event happens or * {@code null} to clear. */ - public void setDropCriteria(String criteriaScript) { - if (!Objects.equals(getState(false).dropCriteria, criteriaScript)) { - getState().dropCriteria = criteriaScript; + public void setDropCriteriaScript(String criteriaScript) { + if (!Objects.equals(getState(false).criteriaScript, criteriaScript)) { + getState().criteriaScript = criteriaScript; } } @@ -182,10 +190,120 @@ public class DropTargetExtension<T extends AbstractComponent> * allowed. * * @return JavaScript that executes when drop event happens. - * @see #setDropCriteria(String) + * @see #setDropCriteriaScript(String) + */ + public String getDropCriteriaScript() { + return getState(false).criteriaScript; + } + + /** + * Set a drop criterion to allow drop on this drop target. When data is + * dragged on top of the drop target, the given value is compared to the + * drag source's payload with the same key. The drag passes this criterion + * if the value of the payload and the value given here are equal. + * <p> + * Note that calling this method will overwrite the previously set criteria. + * To set multiple criteria, call the {@link #setDropCriteria(Criterion.Match, + * Criterion...)} method. + * <p> + * To handle more complex criteria, define a custom script with {@link + * #setDropCriteriaScript(String)}. Drop will be allowed if both this + * criterion and the criteria script are passed. + * + * @param key + * key of the payload to be compared + * @param value + * value to be compared to the payload's value + * @see DragSourceExtension#setPayload(String, String) + */ + public void setDropCriterion(String key, String value) { + setDropCriteria(Criterion.Match.ANY, new Criterion(key, value)); + } + + /** + * Set a drop criterion to allow drop on this drop target. When data is + * dragged on top of the drop target, the given value is compared to the + * drag source's payload with the same key. The drag passes this criterion + * if the value of the payload compared to the given value using the given + * operator holds. + * <p> + * Note that calling this method will overwrite the previously set criteria. + * To set multiple criteria, call the {@link #setDropCriteria(Criterion.Match, + * Criterion...)} method. + * <p> + * To handle more complex criteria, define a custom script with {@link + * #setDropCriteriaScript(String)}. Drop will be allowed if both this + * criterion and the criteria script are passed. + * + * @param key + * key of the payload to be compared + * @param operator + * comparison operator to be used + * @param value + * value to be compared to the payload's value + * @see DragSourceExtension#setPayload(String, int) + */ + public void setDropCriterion(String key, ComparisonOperator operator, + int value) { + setDropCriteria(Criterion.Match.ANY, + new Criterion(key, operator, value)); + } + + /** + * Set a drop criterion to allow drop on this drop target. When data is + * dragged on top of the drop target, the given value is compared to the + * drag source's payload with the same key. The drag passes this criterion + * if the value of the payload compared to the given value using the given + * operator holds. + * <p> + * Note that calling this method will overwrite the previously set criteria. + * To set multiple criteria, call the {@link #setDropCriteria(Criterion.Match, + * Criterion...)} method. + * <p> + * To handle more complex criteria, define a custom script with {@link + * #setDropCriteriaScript(String)}. Drop will be allowed if both this + * criterion and the criteria script are passed. + * + * @param key + * key of the payload to be compared + * @param operator + * comparison operator to be used + * @param value + * value to be compared to the payload's value + * @see DragSourceExtension#setPayload(String, double) + */ + public void setDropCriterion(String key, ComparisonOperator operator, + double value) { + setDropCriteria(Criterion.Match.ANY, + new Criterion(key, operator, value)); + } + + /** + * Sets multiple drop criteria to allow drop on this drop target. When data + * is dragged on top of the drop target, the value of the given criteria is + * compared to the drag source's payload with the same key. + * <p> + * The drag passes these criteria if, depending on {@code match}, any or all + * of the criteria matches the payload, that is the value of the payload + * compared to the value of the criterion using the criterion's operator + * holds. + * <p> + * Note that calling this method will overwrite the previously set + * criteria. + * <p> + * To handle more complex criteria, define a custom script with {@link + * #setDropCriteriaScript(String)}. Drop will be allowed if both this + * criterion and the criteria script are passed. + * + * @param match + * defines whether any or all of the given criteria should match to + * allow drop on this drop target + * @param criteria + * criteria to be compared to the payload */ - public String getDropCriteria() { - return getState(false).dropCriteria; + public void setDropCriteria(Criterion.Match match, Criterion... criteria) { + getState().criteriaMatch = match; + getState().criteria = Arrays.asList(criteria); } /** diff --git a/server/src/main/java/com/vaadin/ui/dnd/event/DragEndEvent.java b/server/src/main/java/com/vaadin/ui/dnd/event/DragEndEvent.java index de0ba64a5e..df71ed0a08 100644 --- a/server/src/main/java/com/vaadin/ui/dnd/event/DragEndEvent.java +++ b/server/src/main/java/com/vaadin/ui/dnd/event/DragEndEvent.java @@ -64,7 +64,7 @@ public class DragEndEvent<T extends AbstractComponent> extends Component.Event { * dragend event. * @see DragSourceExtension#setEffectAllowed(EffectAllowed) * @see DropTargetExtension#setDropEffect(DropEffect) - * @see DropTargetExtension#setDropCriteria(String) + * @see DropTargetExtension#setDropCriteriaScript(String) */ public DropEffect getDropEffect() { return dropEffect; diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/DragSourceState.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/DragSourceState.java index 0fefd05665..42e3bffc07 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/dnd/DragSourceState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/DragSourceState.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import com.vaadin.shared.communication.SharedState; +import com.vaadin.shared.ui.dnd.criteria.Payload; /** * State class containing parameters for DragSourceExtension. @@ -75,4 +76,10 @@ public class DragSourceState extends SharedState { * Used to store data in the {@code DataTransfer} object for the drag event. */ public Map<String, String> data = new HashMap<>(); + + /** + * Payload for comparing against acceptance criteria. Transferred in the + * {@code DataTransfer} object as data type. + */ + public Map<String, Payload> payload = new HashMap<>(); } diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/DropTargetState.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/DropTargetState.java index c36d97f1dc..edd8beddbd 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/dnd/DropTargetState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/DropTargetState.java @@ -15,7 +15,11 @@ */ package com.vaadin.shared.ui.dnd; +import java.util.ArrayList; +import java.util.List; + import com.vaadin.shared.communication.SharedState; +import com.vaadin.shared.ui.dnd.criteria.Criterion; /** * State class containing parameters for DropTargetExtension. @@ -32,5 +36,16 @@ public class DropTargetState extends SharedState { /** * Criteria script to allow drop event on the element */ - public String dropCriteria; + public String criteriaScript; + + /** + * List of criteria to compare against the payload. + */ + public List<Criterion> criteria = new ArrayList<>(); + + /** + * Declares whether any or all of the given criteria should match the + * payload + */ + public Criterion.Match criteriaMatch = Criterion.Match.ANY; } diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/ComparisonOperator.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/ComparisonOperator.java new file mode 100644 index 0000000000..d7ab6ff339 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/ComparisonOperator.java @@ -0,0 +1,55 @@ +/* + * 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.shared.ui.dnd.criteria; + +/** + * Comparison operator for drag and drop acceptance criterion. + * + * @author Vaadin Ltd. + * @since 8.1 + */ +public enum ComparisonOperator { + + /** + * Smaller than operator ("<"). + */ + SMALLER_THAN, + + /** + * Smaller than or equals to operator ("<="). + */ + SMALLER_THAN_OR_EQUALS, + + /** + * Equals to operator ("=="). + */ + EQUALS, + + /** + * Greater than or equals to operator (">="). + */ + GREATER_THAN_OR_EQUALS, + + /** + * Greater than operator (">"). + */ + GREATER_THAN, + + /** + * Not equals operator ("!="). + */ + NOT_EQUALS +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Criterion.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Criterion.java new file mode 100644 index 0000000000..c90637a6e0 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Criterion.java @@ -0,0 +1,215 @@ +/* + * 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.shared.ui.dnd.criteria; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Optional; + +/** + * Stores parameters for the drag and drop acceptance criterion defined using + * the criteria API. + * <p> + * When data is dragged over a drop target, the value here is compared to the + * payload added in DropTargetExtension with same key and value type. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public class Criterion implements Serializable { + + /** + * Declares whether all or any of the given criteria should match when + * compared against the payload. + */ + public enum Match { + /** + * When compared to the payload, the drop will be accepted if any of the + * criteria matches. + */ + ANY, + + /** + * When compared to the payload, the drop will be accepted only if all + * of the given criteria matches. + */ + ALL + } + + private String key; + private String value; + private Payload.ValueType valueType; + private ComparisonOperator operator; + + /** + * Mandatory zero param constructor. + */ + private Criterion() { + + } + + /** + * Creates a criterion object with the default comparison operator {@link + * ComparisonOperator#EQUALS}. + * + * @param key + * key of the payload to be compared + * @param value + * value of the payload to be compared + */ + public Criterion(String key, String value) { + this(key, ComparisonOperator.EQUALS, value, Payload.ValueType.STRING); + } + + /** + * Creates a criterion object. + * + * @param key + * key of the payload to be compared + * @param operator + * comparison operator + * @param value + * value of the payload to be compared + */ + public Criterion(String key, ComparisonOperator operator, int value) { + this(key, operator, String.valueOf(value), Payload.ValueType.INTEGER); + } + + /** + * Creates a criterion object. + * + * @param key + * key of the payload to be compared + * @param operator + * comparison operator + * @param value + * value of the payload to be compared + */ + public Criterion(String key, ComparisonOperator operator, double value) { + this(key, operator, String.valueOf(value), Payload.ValueType.DOUBLE); + } + + /** + * Creates a criterion object. + * + * @param key + * key of the payload to be compared + * @param operator + * comparison operator + * @param value + * value of the payload to be compared + * @param valueType + * type of the payload to be compared + */ + private Criterion(String key, ComparisonOperator operator, String value, + Payload.ValueType valueType) { + this.key = key; + this.value = value; + this.valueType = valueType; + this.operator = operator; + } + + /** + * Gets the key of the payload to be compared + * + * @return key of the payload to be compared + */ + public String getKey() { + return key; + } + + /** + * Gets the value of the payload to be compared + * + * @return value of the payload to be compared + */ + public String getValue() { + return value; + } + + /** + * Gets the type of the payload value to be compared + * + * @return type of the payload value to be compared + */ + public Payload.ValueType getValueType() { + return valueType; + } + + /** + * Gets the comparison operator. + * + * @return operator to be used when comparing payload value with criterion + */ + public ComparisonOperator getOperator() { + return operator; + } + + /** + * Compares this criterion's value to the given payload's value and returns + * whether the result matches the criterion's operator. The comparison is + * done with the payload whose key and value type match the criterion's key + * and value type. + * + * @param payloadCollection + * collection of payloads to compare the criterion against + * @return {@code false} if there exists a payload in the collection with + * the same key and value type and it doesn't match the criterion, {@code + * true} otherwise + */ + public boolean resolve(Collection<Payload> payloadCollection) { + Optional<Payload> payload = payloadCollection.stream() + .filter(p -> p.getKey().equals(key) && p.getValueType() + .equals(valueType)).findAny(); + + return payload.map(this::compareCriterionValue).orElse(true); + } + + private boolean compareCriterionValue(Payload payload) { + int result; + + switch (valueType) { + case STRING: + default: + result = value.compareTo(payload.getValue()); + break; + case INTEGER: + result = Integer.valueOf(value) + .compareTo(Integer.valueOf(payload.getValue())); + break; + case DOUBLE: + result = Double.valueOf(value) + .compareTo(Double.valueOf(payload.getValue())); + break; + } + + switch (operator) { + case SMALLER_THAN: + return result < 0; + case SMALLER_THAN_OR_EQUALS: + return result <= 0; + case EQUALS: + default: + return result == 0; + case GREATER_THAN_OR_EQUALS: + return result >= 0; + case GREATER_THAN: + return result > 0; + case NOT_EQUALS: + return result != 0; + } + } +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Payload.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Payload.java new file mode 100644 index 0000000000..ffa3d0a1e6 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/criteria/Payload.java @@ -0,0 +1,125 @@ +/* + * 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.shared.ui.dnd.criteria; + +import java.io.Serializable; + +/** + * Stores key/value pairs and the value type. Payload is set in + * DragSourceExtension and is transferred during drag operation. It is used for + * comparing values to acceptance criteria. + */ +public class Payload implements Serializable { + + /** + * Type of the payload's value. + */ + public enum ValueType { + STRING, INTEGER, DOUBLE; + } + + /** + * Prefix of the payload data type. + */ + public static final String ITEM_PREFIX = "v-item"; + + private String key; + private String value; + private ValueType valueType; + + /** + * Mandatory zero arg constructor. + */ + private Payload() { + + } + + /** + * Creates a payload object. + * + * @param key + * key of the payload + * @param value + * value of the payload + * @param valueType + * type of the payload value + */ + public Payload(String key, String value, ValueType valueType) { + this.key = key; + this.value = value; + this.valueType = valueType; + } + + /** + * Gets the key of this payload. + * + * @return key identifying this payload + */ + public String getKey() { + return key; + } + + /** + * Gets the value of this payload. + * + * @return value of this payload + */ + public String getValue() { + return value; + } + + /** + * Gets the value type of this payload. + * + * @return the type of the value of this payload + */ + public ValueType getValueType() { + return valueType; + } + + /** + * Returns the string representation of this payload. It is used as the data + * type in the {@code DataTransfer} object. + * + * @return the string representation of this payload + */ + public String getPayloadString() { + return ITEM_PREFIX + ":" + valueType.name().toLowerCase() + ":" + key + + ":" + value; + } + + /** + * Parses a payload string and returns a payload object represented by that + * string. + * + * @param payloadString + * string that represents a payload object + * @return a payload object represented by the given string + */ + public static Payload parse(String payloadString) { + String[] parts = payloadString.split(":"); + + if (parts.length != 4 || !ITEM_PREFIX.equals(parts[0])) { + throw new IllegalArgumentException( + "Data type does not have a valid payload format"); + } + + // Create payload object of the given parts. Value type is converted to + // upper case to match the enum's case. + return new Payload(parts[2], parts[3], + ValueType.valueOf(parts[1].toUpperCase())); + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/dnd/DragAndDropCardShuffle.java b/uitest/src/main/java/com/vaadin/tests/dnd/DragAndDropCardShuffle.java index 493b57226e..8f04ed1835 100644 --- a/uitest/src/main/java/com/vaadin/tests/dnd/DragAndDropCardShuffle.java +++ b/uitest/src/main/java/com/vaadin/tests/dnd/DragAndDropCardShuffle.java @@ -22,6 +22,7 @@ import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; import com.vaadin.server.Page; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.dnd.criteria.ComparisonOperator; import com.vaadin.shared.ui.dnd.DropEffect; import com.vaadin.shared.ui.dnd.EffectAllowed; import com.vaadin.tests.components.AbstractTestUIWithLog; @@ -86,20 +87,20 @@ public class DragAndDropCardShuffle extends AbstractTestUIWithLog { // Create UI and add extensions ace.setStyleName("card"); - addDragSourceExtension(ace); - addDropTargetExtension(ace); + addDragSourceExtension(ace, 14); + addDropTargetExtension(ace, 14); jack.setStyleName("card"); - addDragSourceExtension(jack); - addDropTargetExtension(jack); + addDragSourceExtension(jack, 11); + addDropTargetExtension(jack, 11); queen.setStyleName("card"); - addDragSourceExtension(queen); - addDropTargetExtension(queen); + addDragSourceExtension(queen, 12); + addDropTargetExtension(queen, 12); king.setStyleName("card"); - addDragSourceExtension(king); - addDropTargetExtension(king); + addDragSourceExtension(king, 13); + addDropTargetExtension(king, 13); } private void removeExtensions() { @@ -116,11 +117,14 @@ public class DragAndDropCardShuffle extends AbstractTestUIWithLog { king.removeExtension(king.getExtensions().iterator().next()); } - private void addDragSourceExtension(Label source) { + private void addDragSourceExtension(Label source, int cardValue) { // Create and attach extension DragSourceExtension<Label> dragSource = new DragSourceExtension<>( source); + // Set card value via criteria API for acceptance criteria + dragSource.setPayload("card_value", cardValue); + // Add listeners dragSource.addDragStartListener(event -> { log(event.getComponent().getValue() + " dragstart, effectsAllowed=" @@ -135,11 +139,15 @@ public class DragAndDropCardShuffle extends AbstractTestUIWithLog { sources.add(dragSource); } - private void addDropTargetExtension(Label target) { + private void addDropTargetExtension(Label target, int cardValue) { // Create and attach extension DropTargetExtension<Label> dropTarget = new DropTargetExtension<>( target); + // Cards can be dropped onto others with smaller value + dropTarget.setDropCriterion("card_value", ComparisonOperator.SMALLER_THAN, + cardValue); + // Add listener dropTarget.addDropListener(event -> { event.getDragSourceExtension().ifPresent(dragSource -> { |