/* * Copyright 2000-2016 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.ui; 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.shared.AbstractFieldState; import com.vaadin.shared.Registration; import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * 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.v7.ui.AbstractField}, for compatibility and migration * purposes. * * @author Vaadin Ltd. * @since 8.0 * * @param <T> * the input value type */ public abstract class AbstractField<T> extends AbstractComponent implements HasValue<T>, Focusable { @Override public void setValue(T value) { setValue(value, false); } @Override public boolean isReadOnly() { return super.isReadOnly(); } /** * {@inheritDoc} * <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<T> listener) { return addListener(ValueChangeEvent.class, listener, ValueChangeListener.VALUE_CHANGE_METHOD); } @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, isReadOnly(), def.isReadOnly(), Boolean.class, designContext); } @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 * @param userOriginated * {@code true} if this event originates from the client, * {@code false} otherwise. * @return <code>true</code> if the value was updated, <code>false</code> * otherwise */ protected boolean setValue(T value, boolean userOriginated) { if (userOriginated && isReadOnly()) { return false; } if (!isDifferentValue(value)) { return false; } T oldValue = this.getValue(); doSetValue(value); if (!userOriginated) { markAsDirty(); } fireEvent(createValueChange(oldValue, userOriginated)); return true; } /** * Called when a new value is set to determine whether the provided new * value is considered to be a change compared to the current value. This is * used to determine whether {@link #doSetValue(Object)} should be called * and a value change event fired. * * @param newValue * the new value candidate to check, may be <code>null</code> * * @return <code>true</code> if the provided value is considered to be * different and a value change event should be fired; * <code>false</code> if the values are considered to be the same * and no value change should be fired */ protected boolean isDifferentValue(T newValue) { return !Objects.equals(newValue, getValue()); } /** * 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 oldValue * the value of this field before this value change event * @param userOriginated * {@code true} if this event originates from the client, * {@code false} otherwise. * @return the new event */ protected ValueChangeEvent<T> createValueChange(T oldValue, boolean userOriginated) { return new ValueChangeEvent<>(this, oldValue, userOriginated); } @Override protected AbstractFieldState getState() { return (AbstractFieldState) super.getState(); } @Override protected AbstractFieldState getState(boolean markAsDirty) { return (AbstractFieldState) super.getState(markAsDirty); } @Override public void focus() { super.focus(); } @Override public int getTabIndex() { return getState(false).tabIndex; } @Override public void setTabIndex(int tabIndex) { getState().tabIndex = tabIndex; } @Override public void setRequiredIndicatorVisible(boolean visible) { super.setRequiredIndicatorVisible(visible); } @Override public boolean isRequiredIndicatorVisible() { return super.isRequiredIndicatorVisible(); } }