/** * Copyright 2009-2013 Oy 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.addon.jpacontainer.fieldfactory; import com.vaadin.v7.data.Property; import com.vaadin.v7.data.util.AbstractProperty; import com.vaadin.v7.data.util.PropertyFormatter; import com.vaadin.v7.data.util.converter.Converter.ConversionException; /** * PropertyTranslator is bit like the {@link PropertyFormatter}, but works also * for other than string fields. * <p> * Typical use case is where you have a select whose value is an Entity, but the * container datasource used in the select uses a different identifier (most * commonly entity id in database). */ public abstract class PropertyTranslator extends AbstractProperty implements Property.ValueChangeListener, Property.ReadOnlyStatusChangeListener, Property.Viewer { /** Datasource that stores the actual value. */ Property dataSource; /** * Construct a new {@code PropertyTranslator} that is not connected to any * data source. Call {@link #setPropertyDataSource(Property)} later on to * attach it to a property. * */ protected PropertyTranslator() { } /** * Construct a new translator that is connected to given data source. Calls * {@link #translateFromDatasource(Object)} which can be a problem if the * formatter has not yet been initialized. * * @param propertyDataSource * to connect this property to. */ public PropertyTranslator(Property propertyDataSource) { setPropertyDataSource(propertyDataSource); } /** * Gets the current data source of the translator, if any. * * @return the current data source as a Property, or <code>null</code> if * none defined. */ public Property getPropertyDataSource() { return dataSource; } /** * Sets the specified Property as the data source for the translator. * * @param newDataSource * the new data source Property. */ public void setPropertyDataSource(Property newDataSource) { boolean readOnly = false; Object prevValue = null; if (dataSource != null) { if (dataSource instanceof Property.ValueChangeNotifier) { ((Property.ValueChangeNotifier) dataSource) .removeListener(this); } if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) { ((Property.ReadOnlyStatusChangeNotifier) dataSource) .removeListener(this); } readOnly = isReadOnly(); prevValue = getValue(); } dataSource = newDataSource; if (dataSource != null) { if (dataSource instanceof Property.ValueChangeNotifier) { ((Property.ValueChangeNotifier) dataSource).addListener(this); } if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) { ((Property.ReadOnlyStatusChangeNotifier) dataSource) .addListener(this); } } if (isReadOnly() != readOnly) { fireReadOnlyStatusChange(); } Object newVal = getValue(); if ((prevValue == null && newVal != null) || (prevValue != null && !prevValue.equals(newVal))) { fireValueChange(); } } /* Documented in the interface */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Class getType() { return String.class; } /** * Get the translated value. * * @return the translated value */ public Object getValue() { Object value = dataSource == null ? null : dataSource.getValue(); if (value == null) { return null; } return translateFromDatasource(value); } /** * Get the translated value as string. * * @return If the datasource returns null, this is null. Otherwise this is * translateFromDatasource.toString(). */ @Override public String toString() { Object value = dataSource == null ? null : dataSource.getValue(); if (value == null) { return null; } return translateFromDatasource(value).toString(); } /** Reflects the read-only status of the datasource. */ public boolean isReadOnly() { return dataSource == null ? false : dataSource.isReadOnly(); } /** * This method must be implemented to translate the value received from * DataSource. * * @param value * Value object got from the datasource. This is guaranteed to be * non-null and of the type compatible with getType() of the * datasource. * @return */ abstract public Object translateFromDatasource(Object value); /** * This method is used by setValue() method to translate given value to be * suitable for the datasource. * * The method is required to assure that * translateToDatasource(translateFromDatasource(x)) equals x. * * @param translatedValue * this is the value set by user of the formatter (typically * field). * @return Non-null value compatible with datasource. * @throws Exception * Any type of exception can be thrown to indicate that the * conversion was not succesful. */ abstract public Object translateToDatasource(Object formattedValue) throws Exception; /** * Sets the Property's read-only mode to the specified status. * * @param newStatus * the new read-only status of the Property. */ public void setReadOnly(boolean newStatus) { if (dataSource != null) { dataSource.setReadOnly(newStatus); } } public void setValue(Object newValue) throws ReadOnlyException, ConversionException { if (dataSource == null) { return; } if (newValue == null) { if (dataSource.getValue() != null) { dataSource.setValue(null); if (!(dataSource instanceof ValueChangeNotifier)) { fireValueChange(); } } } else { try { dataSource.setValue(translateToDatasource(newValue)); if (!(dataSource instanceof ValueChangeNotifier)) { fireValueChange(); } } catch (Exception e) { if (e instanceof ConversionException) { throw (ConversionException) e; } else { throw new ConversionException(e); } } } } /** * Listens for changes in the datasource. * * This should not be called directly. */ public void valueChange(com.vaadin.v7.data.Property.ValueChangeEvent event) { fireValueChange(); } /** * Listens for changes in the datasource. * * This should not be called directly. */ public void readOnlyStatusChange( com.vaadin.v7.data.Property.ReadOnlyStatusChangeEvent event) { fireReadOnlyStatusChange(); } }