Fixes #9600tags/8.1.0.rc1
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
/** |
@@ -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) { | |||
@@ -293,6 +295,73 @@ public class DragSourceExtension<T extends AbstractComponent> | |||
getState().data.clear(); | |||
} | |||
/** | |||
* 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 | |||
@@ -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); |
@@ -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); | |||
} | |||
/** |
@@ -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; |
@@ -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<>(); | |||
} |
@@ -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; | |||
} |
@@ -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 | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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())); | |||
} | |||
} |
@@ -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 -> { |