Package DisplayProject.binding

Source Code of DisplayProject.binding.ObjectTableModel$RowSourceValueChangeHandler

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package DisplayProject.binding;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;

import javax.swing.JTable;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;

import DisplayProject.ArrayColumn;
import DisplayProject.ArrayColumnModel;
import DisplayProject.ArrayFieldModel;
import DisplayProject.actions.ActionMgr;
import DisplayProject.actions.PendingAction;
import DisplayProject.binding.value.ValueModel;
import Framework.DataValue;
import Framework.DynamicArray;
import Framework.EventManager;
import Framework.NumericData;

/**
* A specialised table model that uses bean introspection to get and set values and
* a row source represented by a ValueModel providing the ability for the table model
* to update itself if the row source is replaced externally
*
* In addition can observe changes made externally to elements of the bound dynamic
* array.
*/
@SuppressWarnings("serial")
public class ObjectTableModel extends ArrayFieldModel
{
    private ValueModel rowSource;
    private PropertyChangeListener rowSourceChangeHandler = new RowSourceValueChangeHandler(this);
    private PropertyChangeListener elementChangedHandler = new ElementChangeHandler(this);
    private String[] propertyNames;
    private BeanWrapper beanWrapper;
    private DynamicArrayElementObserver elementObserver;
    private static final Logger _log = Logger.getLogger(ObjectTableModel.class);
    /**
     * If the valueIsAdjusting flag is true, then changes to a bound value will not fire appropriate
     * table change events, as it is expected that the code that has set this flag to true will fire
     * the events as appropriate
     */
    private boolean valueIsAdjusting = false;

    /**
     * Creates a new table model for the specified bean type
     * @param table
     * @param beanType
     * @param propertyNames
     */
    public ObjectTableModel(JTable table, Class<?> beanType, String[] propertyNames)
    {
        super(table);
        this.beanWrapper = new BeanWrapperImpl(beanType);
        this.propertyNames = propertyNames;
        elementObserver = new DynamicArrayElementObserver(elementChangedHandler, propertyNames);
    }

    public Object newRow() {
        Object row = BeanUtils.instantiateClass(this.beanWrapper.getWrappedClass());
        getData().add(row);
        return row;
    }

    @SuppressWarnings("unchecked")
  public void setRowSource(ValueModel rowSource)
    {
        this.rowSource = rowSource;
        this.rowSource.addValueChangeListener(rowSourceChangeHandler);
        setDataAndRegisterListeners((DynamicArray<Object>) rowSource.getValue());
    }

    /* (non-Javadoc)
     * @see javax.swing.table.TableModel#getValueAt(int, int)
     */
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        Object result;
        try {
            if (getData() != null) {

                Object data = getData().get(rowIndex);
                if (data != null) {
                  // TF:05/10/2009:DET-106:Made this thread-safe.
                  if (columnIndex > propertyNames.length || "".equals(propertyNames[columnIndex])) {
                    result = null;
                  } else {
                    synchronized(beanWrapper) {
                      beanWrapper.setWrappedInstance(data);
                      result = beanWrapper.getPropertyValue(propertyNames[columnIndex]);
                    }
                  }
                } else {
                    result = null;
                }
            } else {
                result = null;
            }
        } catch (BeansException e) {
            Logger.getLogger(ObjectTableModel.class).debug(e);
            result = "";
        }
        return result;
    }

//    private static class DotInterrogator
//    {
//        //private String fullString;
//        private String beforeDot;
//        private String afterDot;
//
//        DotInterrogator( String fullString, String errMsg)
//        {
//            //this.fullString = fullString;
//            int indexOfDot = fullString.indexOf( '.');
//            Assert.notNull( errMsg);
//            Assert.isTrue( indexOfDot != -1, "Null only expected for nested properties");
//            beforeDot = fullString.substring( 0, indexOfDot);
//            afterDot = fullString.substring( indexOfDot+1);
//        }
//
//        private String getBeforeDot() {
//            return beforeDot;
//        }
//    }

//    private Object constructForNested(BeanWrapper beanWrapper, DotInterrogator dotInterrogator)
//    {
//        Object result;
//        Class clazz = beanWrapper.getPropertyDescriptor( dotInterrogator.getBeforeDot()).getPropertyType();
//        result = FrameworkUtils.newInstance( clazz);
//        return result;
//    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        // Ignore properties that have no mapping.  CraigM 02/09/2007.
        if (getData() != null && this.propertyNames[columnIndex].length() > 0) {
            Object bean = getData().get(rowIndex);
            if (bean != null) {
                beanWrapper.setWrappedInstance(bean);
                try {
                    // Temporarily remove the internal property change listener so a cell
                    // updated event for the cell being edited is the only table event fired
                    DisplayProject.binding.beans.BeanUtils.removePropertyChangeListener(bean, elementChangedHandler);
                    // TF:26/9/07:It is possible that the type our column is not matched to our
                    // underlying data field. For example, we might have a column that is defined as TextData
                    // in the column model, but mapped to a String on the underlying object.
                    Class<?> clazz = beanWrapper.getPropertyType(propertyNames[columnIndex]);
                    Object newValue = TypeConverter.convert(aValue, clazz);
                    Object existingVal = beanWrapper.getPropertyValue(propertyNames[columnIndex]);

                    // CraigM:02/12/2008 - For DataValues, we don't want to replace them, just update them
                    // TF:04/09/2009:Since we're going to be firing a tableCellUpdated event after the value has been
                    // set, prevent other firings of table redraw events.
                    // TF:02/11/2009:Changed this code to be able to update a datavalue from a scalar
                    valueIsAdjusting = true;
                    if (existingVal instanceof DataValue) {
                      if (newValue instanceof DataValue) {
                        ((DataValue)existingVal).setValue((DataValue)newValue);
                      }
                      else if (newValue instanceof Double || newValue instanceof Float) {
                        if (existingVal instanceof NumericData) {
                          ((NumericData)existingVal).setValue(((Number)newValue).doubleValue());
                        }
                        else {
                          ((DataValue)existingVal).setValue(((Number)newValue).toString());
                        }
                      }
                      else if (newValue instanceof Number) {
                        if (existingVal instanceof NumericData) {
                          ((NumericData)existingVal).setValue(((Number)newValue).intValue());
                        }
                        else {
                          ((DataValue)existingVal).setValue(((Number)newValue).toString());
                        }
                      }
                      else if (((DataValue)existingVal).isNullable() && newValue.toString().equals(DataValue.NL_FMSG_NULLVALUE)) {
                        ((DataValue)existingVal).setIsNull(true);
                      }
                      else {
                        ((DataValue)existingVal).setValue(newValue.toString());
                      }
                    }
                    else {
                      beanWrapper.setPropertyValue(propertyNames[columnIndex], newValue);
                    }
                    valueIsAdjusting = false;
                    fireTableCellUpdated(rowIndex, columnIndex);
                } finally {
                    // Make sure we add the property change listener back so changes from outside the
                    // table model are handled
                    if (bean != null) {
                        DisplayProject.binding.beans.BeanUtils.addPropertyChangeListener(bean, elementChangedHandler);
                    }
                }
            }
        }
    }

    public Class<?> getColumnClass(int c)
    {
        Class<?> aClass = beanWrapper.getPropertyType(propertyNames[c]);
        if (aClass != null && aClass.isPrimitive()) {
            // Must return the appropriate Object type
            if (aClass.equals(Boolean.TYPE)) {
                aClass = Boolean.class;
            } else if (aClass.equals(Byte.TYPE)) {
                aClass = Byte.class;
            } else if (aClass.equals(Character.TYPE)) {
                aClass = Character.class;
            } else if (aClass.equals(Short.TYPE)) {
                aClass = Short.class;
            } else if (aClass.equals(Integer.TYPE)) {
                aClass = Integer.class;
            } else if (aClass.equals(Long.TYPE)) {
                aClass = Long.class;
            } else if (aClass.equals(Float.TYPE)) {
                aClass = Float.class;
            } else {
                aClass = Double.class;
            }
        }
        return aClass;
    }

    private void setDataAndRegisterListeners(DynamicArray<Object> data)
    {
        elementObserver.reset(data);
        // Set the table and tell the table to update itself
        setData(data);
    }

    /**
     * Provides a mechanism to automatically notify the view if property
     * being managed by the value model is changed outside of this class
     */
    private static class RowSourceValueChangeHandler implements PropertyChangeListener
    {
      private WeakReference<ObjectTableModel> model;
      // TF:25/06/2010:Changed this class to be static and to provide weak references to the underlying objects. Failure to do
      // this will result in us keeping a window around if the array which was mapped is kept around (since the array has property
      // change listeners, which will keep the object table model in memory, which will keep the table in memory)
      public RowSourceValueChangeHandler(ObjectTableModel otm) {
        model = new WeakReference<ObjectTableModel>(otm);
      }

      @SuppressWarnings("unchecked")
    public void propertyChange(PropertyChangeEvent evt)
        {
          final ObjectTableModel otm = model.get();
          if (otm == null) {
            return;
          }
            // Set the table and tell the table to update itself
            otm.setDataAndRegisterListeners((DynamicArray<Object>) otm.rowSource.getValue());
            if (otm.rowSource.getValue() == null) {
                ActionMgr.addAction(new PendingAction(null) {
                    public void performAction() {
                        otm.fireTableDataChanged();
                    }
                });
            }
        }
    }

    /**
     * An element has changed so tell the  table to update itself. This is only fired when data underlying the
     * table has changed programmatically -- if the user changes the value via the GUI then setValueAt() will be
     * called.
     */
    private static class ElementChangeHandler implements PropertyChangeListener
    {
      private WeakReference<ObjectTableModel> model;
      // TF:25/06/2010:Changed this class to be static and to provide weak references to the underlying objects. Failure to do
      // this will result in us keeping a window around if the array which was mapped is kept around (since the array has property
      // change listeners, which will keep the object table model in memory, which will keep the table in memory)
      public ElementChangeHandler(ObjectTableModel otm) {
        model = new WeakReference<ObjectTableModel>(otm);
      }
     
        public void propertyChange(final PropertyChangeEvent evt)
        {
          // TF:04/09/2009:If we're changing the value as a result of a data update fired from within this class
          // we do not need to update the value in the table, as the calling code will do that later, and it
          // knows which cell to target rather than having to try to derive it.
          final ObjectTableModel otm = model.get();
          if (otm == null) {
            return;
          }
          if (otm.valueIsAdjusting) {
            return;
          }
            ActionMgr.addAction(new PendingAction(null) {
                @Override
                public void performAction() {
                  int selectedColumn = otm.table.getSelectedColumn();
                  int selectedRow = otm.table.getSelectedRow();
              int updatedColumn = -1;
                  int updatedRow = otm.getData().indexOf(evt.getSource());
                  boolean cancelEditingCell = false;
                  String columnAttributeName = evt.getPropertyName();

                  try {
                    // CraigM:22/08/2008 - Corrected checking of column.
                    // CraigM:07/11/2008 - Note: If an object was replaced that maps to 0 or more columns, then updatedColumn will be set to -1.
                    if (otm.table.getColumnModel() instanceof ArrayColumnModel) {
                      ArrayColumn ac = ((ArrayColumnModel)otm.table.getColumnModel()).findColumn(columnAttributeName);
                      if (ac != null) {
                        updatedColumn = ac.getModelIndex();
                      }
                    }
                  }
                  catch (Throwable e) {
                    // The data may not even be mapped to a column in the table
                  }

                  // CraigM:21/07/2008 - If we want to update the data in a cell that we are currently editing, we must cancel the editing of the cell
                  if (otm.table.isEditing()) {
                    // We don't have any idea what was updated.  It could affect anything
                    if (updatedColumn == -1 && updatedRow == -1) {
                      cancelEditingCell = true;
                    }
                    // We know something in this row was updated
                    else if (updatedColumn == -1 && updatedRow == selectedRow) {
                      cancelEditingCell = true;
                    }
                    // We know the exact cell that was updated
                    else if (updatedColumn == selectedColumn && updatedRow == selectedRow) {
                      cancelEditingCell = true;
                    }
                  }

                  // CraigM:06/11/2008 - Cancel editing of the cell so the update to the data can occur
                    if (cancelEditingCell) {
                      EventManager.disableEventPosting();
                       otm.table.getCellEditor().cancelCellEditing();
                    }

                    // Debug what is selected and what is being updated
                    _log.debug("Selected ("+selectedColumn+","+selectedRow+"). Updating ("+updatedColumn+","+updatedRow+").");

                    // CraigM:05/11/2008 - If possible, only fire a change to the cells data.  JIRA: JCT-617.
                    if (updatedColumn != -1 && updatedRow != -1) {
                      otm.fireTableCellUpdated(updatedRow, updatedColumn);
                    }
                    // CraigM:07/11/2008 - Or just fire a change to the row
                    else if (updatedColumn == -1 && updatedRow >= 0) {
                      otm.fireTableRowsUpdated(updatedRow, updatedRow);
                    }
                    // CraigM:07/11/2008 - Otherwise, fire a change to the whole table
                    else {
                      otm.fireTableDataChanged();
                    }

                    // CraigM:06/11/2008 - Start the editing again
                    if (cancelEditingCell) {
                      otm.table.changeSelection(selectedRow, selectedColumn, false, false);
                      EventManager.enableEventPosting();
                    }
                }
            });
        }
    }
}
TOP

Related Classes of DisplayProject.binding.ObjectTableModel$RowSourceValueChangeHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.