/** * Copyright (C) 2015 Valkyrie RCP * * 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 org.valkyriercp.binding.form.support; import org.valkyriercp.binding.form.FieldMetadata; import org.valkyriercp.binding.form.FormModel; import org.valkyriercp.binding.value.DirtyTrackingValueModel; import org.valkyriercp.core.support.AbstractPropertyChangePublisher; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Map; /** * Default implementation of FieldMetadata. * <p> * NOTE: This is a framework internal class and should not be * instantiated in user code. * * @author Oliver Hutchison */ public class DefaultFieldMetadata extends AbstractPropertyChangePublisher implements FieldMetadata { private final FormModel formModel; private final DirtyTrackingValueModel valueModel; private final Class propertyType; private final boolean forceReadOnly; private final Map userMetadata = new HashMap(); private boolean oldReadOnly; private boolean readOnly; private boolean enabled = true; private boolean oldEnabled = true; private final DirtyChangeHandler dirtyChangeHandler = new DirtyChangeHandler(); private final PropertyChangeListener formChangeHandler = new FormModelChangeHandler(); /** * Constructs a new instance of DefaultFieldMetadata. * * @param formModel the form model * @param valueModel the value model for the property * @param propertyType the type of the property * @param forceReadOnly should readOnly be forced to true; this is * required if the property can not be modified. e.g. * at the PropertyAccessStrategy level. * @param userMetadata map using String keys containing user defined * metadata. As an example, tiger extensions * currently use this to expose JDK 1.5 annotations on * the backing object as property metadata. This * parameter may be <code>null</code>. */ public DefaultFieldMetadata(FormModel formModel, DirtyTrackingValueModel valueModel, Class propertyType, boolean forceReadOnly, Map userMetadata) { this.formModel = formModel; this.valueModel = valueModel; this.valueModel.addPropertyChangeListener(DirtyTrackingValueModel.DIRTY_PROPERTY, dirtyChangeHandler); this.propertyType = propertyType; this.forceReadOnly = forceReadOnly; this.formModel.addPropertyChangeListener(ENABLED_PROPERTY, formChangeHandler); this.formModel.addPropertyChangeListener(READ_ONLY_PROPERTY, formChangeHandler); this.oldReadOnly = isReadOnly(); this.oldEnabled = isEnabled(); if(userMetadata != null) { this.userMetadata.putAll(userMetadata); } } public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; firePropertyChange(READ_ONLY_PROPERTY, oldReadOnly, isReadOnly()); oldReadOnly = isReadOnly(); } public boolean isReadOnly() { return forceReadOnly || readOnly || formModel.isReadOnly(); } public void setEnabled(boolean enabled) { this.enabled = enabled; firePropertyChange(ENABLED_PROPERTY, oldEnabled, isEnabled()); oldEnabled = isEnabled(); } public boolean isEnabled() { return enabled && formModel.isEnabled(); } public boolean isDirty() { return valueModel.isDirty(); } public Class getPropertyType() { return propertyType; } public Object getUserMetadata(final String key) { return userMetadata.get(key); } public Map getAllUserMetadata() { return userMetadata; } /** * Sets custom metadata to be associated with this property. A property * change event will be fired (from this FieldMetadata, not from the * associated form property) if <code>value</code> differs from the * current value of the specified <code>key</code>. The property change * event will use the value of <code>key</code> as the property name in * the property change event. * * @param key * @param value */ public void setUserMetadata(final String key, final Object value) { final Object old = userMetadata.put(key, value); firePropertyChange(key, old, value); } /** * Clears all custom metadata associated with this property. A property * change event will be fired for every <code>key</code> that contained a * non-null value before this method was invoked. It is possible for a * PropertyChangeListener to mutate user metadata, by setting a key value * for example, in response to one of these property change events fired * during the course of the clear operation. Because of this, there is * no guarantee that all user metadata is in fact completely clear and * empty by the time this method returns. */ public void clearUserMetadata() { // Copy keys into array to avoid concurrent modification exceptions // if any PropertyChangeListeners should modify user metadata during // clear operation. final Object[] keys = userMetadata.keySet().toArray(); for(int i = keys.length - 1;i >= 0;i--) { final Object old = userMetadata.remove(keys[i]); if(old != null) { firePropertyChange((String)keys[i], old, null); } } } /** * Propagates dirty changes from the value model on to * the dirty change listeners attached to this class. */ private class DirtyChangeHandler extends CommitListenerAdapter implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { firePropertyChange(DIRTY_PROPERTY, evt.getOldValue(), evt.getNewValue()); } } /** * Responsible for listening for changes to the enabled * property of the FormModel */ private class FormModelChangeHandler implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { if (FormModel.ENABLED_PROPERTY.equals(evt.getPropertyName())) { firePropertyChange(ENABLED_PROPERTY, Boolean.valueOf(oldEnabled), Boolean.valueOf(isEnabled())); oldEnabled = isEnabled(); } else if (FormModel.READONLY_PROPERTY.equals(evt.getPropertyName())) { firePropertyChange(READ_ONLY_PROPERTY, Boolean.valueOf(oldReadOnly), Boolean.valueOf(isReadOnly())); oldReadOnly = isReadOnly(); } } } }