Browse Source

Implement new simple AbstractField

Change-Id: I9addcf8bec802967b1dfa39512dd140b8a4e4a25
tags/8.0.0.alpha1
Johannes Dahlström 7 years ago
parent
commit
5d864b14b3

+ 153
- 0
server/src/main/java/com/vaadin/data/HasValue.java View File

@@ -0,0 +1,153 @@
/*
* 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.data;

import java.io.Serializable;
import java.util.function.Consumer;

import com.vaadin.event.ConnectorEvent;
import com.vaadin.event.ConnectorEventListener;
import com.vaadin.event.Registration;
import com.vaadin.server.ClientConnector;

/**
* A generic interface for field components and other user interface objects
* that have a user-editable value. Emits change events whenever the value is
* changed, either by the user or programmatically.
*
* @since
* @param <V>
* the value type
*/
public interface HasValue<V> extends Serializable {

/**
* An event fired when the value of a {@code HasValue} changes.
*
* @param <V>
* the value type
*/
public class ValueChange<V> extends ConnectorEvent {

private final V value;
private final boolean userOriginated;

/**
* Creates a new {@code ValueChange} event containing the current value
* of the given value-bearing source connector.
*
* @param <C>
* the type of the source connector
* @param source
* the source connector bearing the value, not null
* @param userOriginated
* {@code true} if this event originates from the client,
* {@code false} otherwise.
*/
public <C extends ClientConnector & HasValue<V>> ValueChange(
C source, boolean userOriginated) {
super(source);
this.value = source.getValue();
this.userOriginated = userOriginated;
}

/**
* Returns the new value of the source connector.
*
* @return the new value
*/
public V getValue() {
return value;
}

/**
* Returns whether this event was triggered by user interaction, on the
* client side, or programmatically, on the server side.
*
* @return {@code true} if this event originates from the client,
* {@code false} otherwise.
*/
public boolean isUserOriginated() {
return userOriginated;
}
}

/**
* A listener for value change events.
*
* @param <V>
* the value type
*
* @see ValueChange
* @see Registration
*/
@FunctionalInterface
public interface ValueChangeListener<V> extends Consumer<ValueChange<V>>,
ConnectorEventListener {

/**
* Invoked when this listener receives a value change event from an
* event source to which it has been added.
*
* @param event
* the received event, not null
*/
// In addition to customizing the Javadoc, this override is needed
// to make ReflectTools.findMethod work as expected. It uses
// Class.getDeclaredMethod, but even if it used getMethod instead, the
// superinterface argument type is Object, not Event, after type
// erasure.
@Override
public void accept(ValueChange<V> event);
}

/**
* Sets the value of this object. If the new value is not equal to
* {@code getValue()}, fires a value change event. May throw
* {@code IllegalArgumentException} if the value is not acceptable.
* <p>
* <i>Implementation note:</i> the implementing class should document
* whether null values are accepted or not.
*
* @param value
* the new value
* @throws IllegalArgumentException
* if the value is invalid
*/
public void setValue(V value);

/**
* Returns the current value of this object.
* <p>
* <i>Implementation note:</i> the implementing class should document
* whether null values may be returned or not.
*
* @return the current value
*/
public V getValue();

/**
* Adds an {@link ValueChangeListener}. The listener is called when the
* value of this {@code hasValue} is changed either by the user or
* programmatically.
*
* @param listener
* the value change listener, not null
* @return a registration for the listener
*/
public Registration addValueChangeListener(
ValueChangeListener<? super V> listener);
}

+ 18
- 0
server/src/main/java/com/vaadin/event/ConnectorEvent.java View File

@@ -20,11 +20,29 @@ import java.util.EventObject;

import com.vaadin.server.ClientConnector;

/**
* A base class for user interface events fired by connectors.
*
* @author Vaadin Ltd.
* @since 7.0
*/
public abstract class ConnectorEvent extends EventObject {

/**
* Creates a new event fired by the given source.
*
* @param source
* the source connector
*/
public ConnectorEvent(ClientConnector source) {
super(source);
}

/**
* Returns the connector that fired the event.
*
* @return the source connector
*/
public ClientConnector getConnector() {
return (ClientConnector) getSource();
}

+ 33
- 0
server/src/main/java/com/vaadin/event/Registration.java View File

@@ -0,0 +1,33 @@
/*
* 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.event;

import java.io.Serializable;

/**
* A registration object for removing an event listener added to a source.
*
* @author Vaadin Ltd.
* @since
*/
@FunctionalInterface
public interface Registration extends Serializable {

/**
* Removes the associated listener from the event source.
*/
public void remove();
}

+ 29
- 22
server/src/main/java/com/vaadin/legacy/ui/LegacyAbstractField.java View File

@@ -62,10 +62,10 @@ import com.vaadin.ui.declarative.DesignContext;
* </p>
*
* <p>
* LegacyAbstractField also provides the {@link com.vaadin.data.Buffered} interface
* for buffering the data source value. By default the LegacyField is in write
* through-mode and {@link #setWriteThrough(boolean)}should be called to enable
* buffering.
* LegacyAbstractField also provides the {@link com.vaadin.data.Buffered}
* interface for buffering the data source value. By default the LegacyField is
* in write through-mode and {@link #setWriteThrough(boolean)}should be called
* to enable buffering.
* </p>
*
* <p>
@@ -75,10 +75,17 @@ import com.vaadin.ui.declarative.DesignContext;
*
* @author Vaadin Ltd.
* @since 3.0
*
* @deprecated This class is, apart from the rename, identical to the Vaadin 7
* {@code com.vaadin.ui.AbstractField}. It is provided for
* compatibility and migration purposes. As of 8.0, new field
* implementations should extend the new
* {@link com.vaadin.ui.AbstractField} instead.
*/
@SuppressWarnings("serial")
public abstract class LegacyAbstractField<T> extends AbstractComponent implements
LegacyField<T>, Property.ReadOnlyStatusChangeListener,
@Deprecated
public abstract class LegacyAbstractField<T> extends AbstractComponent
implements LegacyField<T>, Property.ReadOnlyStatusChangeListener,
Property.ReadOnlyStatusChangeNotifier, Action.ShortcutNotifier {

/* Private members */
@@ -187,11 +194,11 @@ public abstract class LegacyAbstractField<T> extends AbstractComponent implement
}

/**
* Returns the type of the LegacyField. The methods <code>getValue</code> and
* <code>setValue</code> must be compatible with this type: one must be able
* to safely cast the value returned from <code>getValue</code> to the given
* type and pass any variable assignable to this type as an argument to
* <code>setValue</code>.
* Returns the type of the LegacyField. The methods <code>getValue</code>
* and <code>setValue</code> must be compatible with this type: one must be
* able to safely cast the value returned from <code>getValue</code> to the
* given type and pass any variable assignable to this type as an argument
* to <code>setValue</code>.
*
* @return the type of the LegacyField
*/
@@ -834,9 +841,9 @@ public abstract class LegacyAbstractField<T> extends AbstractComponent implement
* Returns the current value (as returned by {@link #getValue()}) converted
* to the data source type.
* <p>
* This returns the same as {@link LegacyAbstractField#getValue()} if no converter
* has been set. The value is not necessarily the same as the data source
* value e.g. if the field is in buffered mode and has been modified.
* This returns the same as {@link LegacyAbstractField#getValue()} if no
* converter has been set. The value is not necessarily the same as the data
* source value e.g. if the field is in buffered mode and has been modified.
* </p>
*
* @return The converted value that is compatible with the data source type
@@ -870,7 +877,7 @@ public abstract class LegacyAbstractField<T> extends AbstractComponent implement
@Override
public void addValidator(Validator validator) {
if (validators == null) {
validators = new LinkedList<Validator>();
validators = new LinkedList<>();
}
validators.add(validator);
markAsDirty();
@@ -993,7 +1000,7 @@ public abstract class LegacyAbstractField<T> extends AbstractComponent implement
}
}

List<InvalidValueException> validationExceptions = new ArrayList<InvalidValueException>();
List<InvalidValueException> validationExceptions = new ArrayList<>();
if (validators != null) {
// Gets all the validation errors
for (Validator v : validators) {
@@ -1384,10 +1391,10 @@ public abstract class LegacyAbstractField<T> extends AbstractComponent implement
}

/**
* Sets the internal field value. This is purely used by LegacyAbstractField to
* change the internal LegacyField value. It does not trigger valuechange events.
* It can be overridden by the inheriting classes to update all dependent
* variables.
* Sets the internal field value. This is purely used by LegacyAbstractField
* to change the internal LegacyField value. It does not trigger valuechange
* events. It can be overridden by the inheriting classes to update all
* dependent variables.
*
* Subclasses can also override {@link #getInternalValue()} if necessary.
*
@@ -1619,8 +1626,8 @@ public abstract class LegacyAbstractField<T> extends AbstractComponent implement

/**
* A ready-made {@link ShortcutListener} that focuses the given
* {@link Focusable} (usually a {@link LegacyField}) when the keyboard shortcut is
* invoked.
* {@link Focusable} (usually a {@link LegacyField}) when the keyboard
* shortcut is invoked.
*
*/
public static class FocusShortcut extends ShortcutListener {

+ 22
- 12
server/src/main/java/com/vaadin/legacy/ui/LegacyField.java View File

@@ -17,32 +17,37 @@
package com.vaadin.legacy.ui;

import com.vaadin.data.BufferedValidatable;
import com.vaadin.data.HasValue.ValueChange;
import com.vaadin.data.Property;
import com.vaadin.ui.Component;
import com.vaadin.ui.Component.Event;
import com.vaadin.ui.Component.Focusable;

/**
* LegacyField interface is implemented by all classes (field components) that have a
* value that the user can change through the user interface.
* LegacyField interface is implemented by all legacy field components that have
* a value that the user can change through the user interface.
*
* LegacyField components are built upon the framework defined in the LegacyField interface
* and the {@link com.vaadin.LegacyAbstractField} base class.
* LegacyField components are built upon the framework defined in the
* LegacyField interface and the {@link com.vaadin.LegacyAbstractField} base
* class.
*
* The LegacyField interface inherits the {@link com.vaadin.ui.Component}
* superinterface and also the {@link com.vaadin.ui.Property} interface to have
* a value for the field.
*
*
* @author Vaadin Ltd.
*
* @param T
* @param <T>
* the type of values in the field, which might not be the same type
* as that of the data source if converters are used
*
* @author IT Mill Ltd.
* @deprecated This interface is, apart from the rename, identical to the Vaadin
* 7 {@code com.vaadin.ui.Field}. It is provided for compatibility
* and migration purposes. As of 8.0, new field components should
* extend {@link com.vaadin.ui.AbstractField} instead.
*/
public interface LegacyField<T> extends Component, BufferedValidatable, Property<T>,
@Deprecated
public interface LegacyField<T> extends Component, BufferedValidatable,
Property<T>,
Property.ValueChangeNotifier, Property.ValueChangeListener,
Property.Editor, Focusable {

@@ -85,14 +90,18 @@ public interface LegacyField<T> extends Component, BufferedValidatable, Property
public String getRequiredError();

/**
* An <code>Event</code> object specifying the LegacyField whose value has been
* changed.
* An <code>Event</code> object specifying the LegacyField whose value has
* been changed.
*
* @author Vaadin Ltd.
* @since 3.0
*
* @deprecated As of 8.0, replaced by {@link ValueChange}.
*/
@Deprecated
@SuppressWarnings("serial")
public static class ValueChangeEvent extends Component.Event implements
public static class ValueChangeEvent extends
Component.Event implements
Property.ValueChangeEvent {

/**
@@ -114,6 +123,7 @@ public interface LegacyField<T> extends Component, BufferedValidatable, Property
public Property getProperty() {
return (Property) getSource();
}

}

/**

+ 182
- 0
server/src/main/java/com/vaadin/ui/AbstractField.java View File

@@ -0,0 +1,182 @@
/*
* 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.ui;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Objects;

import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;

import com.vaadin.data.HasValue;
import com.vaadin.event.Registration;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.util.ReflectTools;

/**
* An abstract implementation of a field, or a {@code Component} allowing user
* input. Implements {@link HasValue} to represent the input value. Examples of
* typical field components include text fields, date pickers, and check boxes.
* <p>
* This class replaces the Vaadin 7 {@code com.vaadin.ui.AbstractField} class.
* The old {@code AbstractField} is retained, under the new name
* {@link com.vaadin.legacy.ui.LegacyAbstractField}, for compatibility and
* migration purposes.
*
* @author Vaadin Ltd.
* @since
*
* @param <T>
* the input value type
*/
public abstract class AbstractField<T> extends AbstractComponent
implements HasValue<T> {

@Deprecated
private static final Method VALUE_CHANGE_METHOD = ReflectTools.findMethod(
ValueChangeListener.class, "accept", ValueChange.class);

@Override
public void setValue(T value) {
setValue(value, false);
}

/**
* Returns whether the value of this field can be changed by the user or
* not. By default fields are not read-only.
*
* @return {@code true} if this field is in read-only mode, {@code false}
* otherwise.
*
* @see #setReadOnly(boolean)
*/
@Override
public boolean isReadOnly() {
return super.isReadOnly();
}

/**
* Sets whether the value of this field can be changed by the user or not. A
* field in read-only mode typically looks visually different to signal to
* the user that the value cannot be edited.
* <p>
* The server ignores (potentially forged) value change requests from the
* client to fields that are read-only. Programmatically changing the field
* value via {@link #setValue(T)} is still possible.
* <p>
* The read-only mode is distinct from the
* {@linkplain Component#setEnabled(boolean) disabled} state. When disabled,
* a component cannot be interacted with at all, and its content should be
* considered irrelevant or not applicable. In contrast, the user should
* still be able to read the content and otherwise interact with a read-only
* field even though changing the value is disallowed.
*
* @param readOnly
* {@code true} to set read-only mode, {@code false} otherwise.
*/
@Override
public void setReadOnly(boolean readOnly) {
super.setReadOnly(readOnly);
}

@Override
public Registration addValueChangeListener(
ValueChangeListener<? super T> listener) {
Objects.requireNonNull(listener, "listener cannot be null");
addListener(ValueChange.class, listener, VALUE_CHANGE_METHOD);
return () -> removeListener(ValueChange.class, listener);
}

@Override
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
Attributes attr = design.attributes();
if (attr.hasKey("readonly")) {
setReadOnly(DesignAttributeHandler.readAttribute("readonly", attr,
Boolean.class));
}
}

@Override
public void writeDesign(Element design, DesignContext designContext) {
super.writeDesign(design, designContext);
AbstractField<T> def = designContext.getDefaultInstance(this);
Attributes attr = design.attributes();
DesignAttributeHandler.writeAttribute("readonly", attr,
super.isReadOnly(), def.isReadOnly(), Boolean.class);
}

@Override
protected Collection<String> getCustomAttributes() {
Collection<String> attributes = super.getCustomAttributes();
attributes.add("readonly");
// must be handled by subclasses
attributes.add("value");
return attributes;
}

/**
* Sets the value of this field if it has changed and fires a value change
* event. If the value originates from the client and this field is
* read-only, does nothing. Invokes {@link #doSetValue(Object) doSetValue}
* to actually store the value.
*
* @param value
* the new value to set
* @return {@code true} if this event originates from the client,
* {@code false} otherwise.
*/
protected void setValue(T value, boolean userOriginated) {
if (userOriginated && isReadOnly()) {
return;
}
if (Objects.equals(value, getValue())) {
return;
}
doSetValue(value);
if (!userOriginated) {
markAsDirty();
}
fireEvent(createValueChange(userOriginated));
}

/**
* Sets the value of this field. May do sanitization or throw
* {@code IllegalArgumentException} if the value is invalid. Typically saves
* the value to shared state.
*
* @param value
* the new value of the field
* @throws IllegalArgumentException
* if the value is invalid
*/
protected abstract void doSetValue(T value);

/**
* Returns a new value change event instance.
*
* @param userOriginated
* {@code true} if this event originates from the client,
* {@code false} otherwise.
* @return the new event
*/
protected ValueChange<T> createValueChange(boolean userOriginated) {
return new ValueChange<>(this, userOriginated);
}
}

+ 23
- 16
server/src/main/java/com/vaadin/ui/Component.java View File

@@ -379,10 +379,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable {

/**
* Tests whether the component is in the read-only mode. The user can not
* change the value of a read-only component. As only {@link LegacyField}
* components normally have a value that can be input or changed by the
* user, this is mostly relevant only to field components, though not
* restricted to them.
* change the value of a read-only component. As only {@link AbstractField}
* or {@link LegacyField} components normally have a value that can be input
* or changed by the user, this is mostly relevant only to field components,
* though not restricted to them.
*
* <p>
* Notice that the read-only mode only affects whether the user can change
@@ -406,9 +406,9 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* can not change the value of a read-only component.
*
* <p>
* As only {@link LegacyField} components normally have a value that can be input
* or changed by the user, this is mostly relevant only to field components,
* though not restricted to them.
* As only {@link AbstractField} or{@link LegacyField} components normally
* have a value that can be input or changed by the user, this is mostly
* relevant only to field components, though not restricted to them.
* </p>
*
* <p>
@@ -596,7 +596,8 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* public class AttachExample extends CustomComponent {
* public AttachExample() {
* // ERROR: We can't access the application object yet.
* ClassResource r = new ClassResource(&quot;smiley.jpg&quot;, getApplication());
* ClassResource r = new ClassResource(&quot;smiley.jpg&quot;,
* getApplication());
* Embedded image = new Embedded(&quot;Image:&quot;, r);
* setCompositionRoot(image);
* }
@@ -622,7 +623,8 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* super.attach(); // Must call.
*
* // Now we know who ultimately owns us.
* ClassResource r = new ClassResource(&quot;smiley.jpg&quot;, getApplication());
* ClassResource r = new ClassResource(&quot;smiley.jpg&quot;,
* getApplication());
* Embedded image = new Embedded(&quot;Image:&quot;, r);
* setCompositionRoot(image);
* }
@@ -879,7 +881,8 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* getWindow().showNotification(&quot;Click!&quot;);
*
* // Display source component and event class names
* status.setValue(&quot;Event from &quot; + event.getSource().getClass().getName()
* status.setValue(&quot;Event from &quot; + event.getSource().getClass()
* .getName()
* + &quot;: &quot; + event.getClass().getName());
* }
* }
@@ -904,11 +907,13 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* <pre>
* public void componentEvent(Event event) {
* // Act according to the source of the event
* if (event.getSource() == ok &amp;&amp; event.getClass() == Button.ClickEvent.class)
* if (event.getSource() == ok &amp;&amp; event
* .getClass() == Button.ClickEvent.class)
* getWindow().showNotification(&quot;Click!&quot;);
*
* // Display source component and event class names
* status.setValue(&quot;Event from &quot; + event.getSource().getClass().getName()
* status.setValue(&quot;Event from &quot; + event.getSource().getClass()
* .getName()
* + &quot;: &quot; + event.getClass().getName());
* }
* </pre>
@@ -956,7 +961,8 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* if (event.getSource() == ok)
* getWindow().showNotification(&quot;Click!&quot;);
*
* status.setValue(&quot;Event from &quot; + event.getSource().getClass().getName()
* status.setValue(&quot;Event from &quot; + event.getSource().getClass()
* .getName()
* + &quot;: &quot; + event.getClass().getName());
* }
* }
@@ -1028,9 +1034,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* <p>
* Focus can be set with {@link #focus()}. This interface does not provide
* an accessor that would allow finding out the currently focused component;
* focus information can be acquired for some (but not all) {@link LegacyField}
* components through the {@link com.vaadin.event.FieldEvents.FocusListener}
* and {@link com.vaadin.event.FieldEvents.BlurListener} interfaces.
* focus information can be acquired for some (but not all)
* {@link LegacyField} components through the
* {@link com.vaadin.event.FieldEvents.FocusListener} and
* {@link com.vaadin.event.FieldEvents.BlurListener} interfaces.
* </p>
*
* @see FieldEvents

+ 122
- 0
server/src/test/java/com/vaadin/ui/AbstractFieldTest.java View File

@@ -0,0 +1,122 @@
package com.vaadin.ui;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.EasyMockSupport;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.HasValue.ValueChange;
import com.vaadin.data.HasValue.ValueChangeListener;
import com.vaadin.server.ClientConnector;

public class AbstractFieldTest extends EasyMockSupport {

class TextField extends AbstractField<String> {

String value = "";

@Override
public String getValue() {
return value;
}

@Override
protected void doSetValue(String value) {
this.value = value;
}
}

TextField field;

ValueChangeListener<String> l;
Capture<ValueChange<String>> capture;

@Before
public void setUp() {
field = new TextField();
l = mockListener();
capture = new Capture<>();
}

@Test
public void readOnlyFieldAcceptsValueChangeFromServer() {
field.setReadOnly(true);
field.setValue("foo");
assertEquals("foo", field.getValue());
}

@Test
public void readOnlyFieldIgnoresValueChangeFromClient() {
field.setReadOnly(true);
field.setValue("bar", true);
assertEquals("", field.getValue());
}

@Test
public void valueChangeListenerInvoked() {
l.accept(EasyMock.capture(capture));
replayAll();

field.setValue("foo");
field.addValueChangeListener(l);
field.setValue("bar");

assertEventEquals(capture.getValue(), "bar", field, false);

verifyAll();
}

@Test
public void valueChangeListenerInvokedFromClient() {
l.accept(EasyMock.capture(capture));
replayAll();

field.setValue("foo");
field.addValueChangeListener(l);
field.setValue("bar", true);

assertEventEquals(capture.getValue(), "bar", field, true);

verifyAll();
}

@Test
public void valueChangeListenerNotInvokedIfValueUnchanged() {
// expect zero invocations of l
replayAll();

field.setValue("foo");
field.addValueChangeListener(l);
field.setValue("foo");

verifyAll();
}

@Test
public void valueChangeListenerNotInvokedAfterRemove() {
// expect zero invocations of l
replayAll();

field.addValueChangeListener(l).remove();
field.setValue("foo");

verifyAll();
}

@SuppressWarnings("unchecked")
private ValueChangeListener<String> mockListener() {
return createStrictMock(ValueChangeListener.class);
}

private void assertEventEquals(ValueChange<String> e, String value,
ClientConnector source, boolean userOriginated) {
assertEquals("event value", value, e.getValue());
assertSame("event source", source, e.getSource());
assertSame("event source connector", source, e.getConnector());
assertEquals("event from user", userOriginated, e.isUserOriginated());
}
}

Loading…
Cancel
Save