AbstractBinding.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:14k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: AbstractBinding.java,v 1.2 2005/10/10 17:00:50 rbair Exp $
  3.  *
  4.  * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.binding.swingx;
  22. import java.beans.PropertyChangeListener;
  23. import java.beans.PropertyChangeSupport;
  24. import java.util.ArrayList;
  25. import javax.swing.InputVerifier;
  26. import javax.swing.JComponent;
  27. import org.jdesktop.binding.DataModel;
  28. import org.jdesktop.binding.ValueChangeEvent;
  29. import org.jdesktop.binding.ValueChangeListener;
  30. import org.jdesktop.binding.metadata.ConversionException;
  31. import org.jdesktop.binding.metadata.Converter;
  32. import org.jdesktop.binding.metadata.MetaData;
  33. import org.jdesktop.binding.metadata.Validator;
  34. /**
  35.  * Abstract base class which implements a default mechanism for binding
  36.  * user-interface components to elements in a data model.
  37.  *
  38.  * Note: 
  39.  * 
  40.  * @author Amy Fowler
  41.  * @version 1.0
  42.  */
  43. public abstract class AbstractBinding implements Binding {
  44.     protected DataModel dataModel;
  45.     protected MetaData metaData;
  46.     protected String fieldName;
  47.     protected Object cachedValue;
  48.     protected ArrayList errorList;
  49.     protected boolean modified = false;
  50.     protected int validState = UNVALIDATED;
  51.     protected boolean pulling = false;
  52.     protected boolean pushing = false;
  53.     private PropertyChangeSupport pcs;
  54.     private int validationPolicy;
  55.     private static final PropertyChangeListener[] 
  56.         EMPTY_PROPERTY_CHANGE_LISTENER_ARRAY = new PropertyChangeListener[0];
  57.     /**
  58.      * 
  59.      * @param component
  60.      * @param dataModel
  61.      * @param fieldName
  62.      * @param validationPolicy
  63.      * @throws NullPointerException if component, dataModel or
  64.      *     fieldName is null 
  65.      */
  66.     protected AbstractBinding(JComponent component,
  67.                               DataModel dataModel, String fieldName,
  68.                               int validationPolicy) {
  69.         checkNull(component, "component must not be null");
  70.         checkNull(dataModel, "model must not be null");
  71.         checkNull(fieldName, "fieldName must not be null");
  72.         installDataModel(dataModel, fieldName);
  73.         setComponent(component);
  74.         setValidationPolicy(validationPolicy);
  75.     }
  76. //----------------------- implementing Binding
  77.     
  78.     public DataModel getDataModel() {
  79.         return dataModel;
  80.     }
  81.     public String getFieldName() {
  82.         return fieldName;
  83.     }
  84.     /** 
  85.      * 
  86.      * @param policy
  87.      */
  88.     public void setValidationPolicy(int policy) {
  89.         int oldValidationPolicy = this.validationPolicy;
  90.         this.validationPolicy = policy;
  91.         installInputVerifier();
  92.         if (policy != oldValidationPolicy) {
  93.             firePropertyChange("validationPolicy",
  94.                                 new Integer(oldValidationPolicy),
  95.                                 new Integer(policy));
  96.         }
  97.     }
  98.     public int getValidationPolicy() {
  99.         return validationPolicy;
  100.     }
  101.     public boolean pull() {
  102.         pulling = true;
  103. //        if (metaData != null) {
  104.             cachedValue = dataModel.getValue(metaData.getName());
  105.             setComponentValue(cachedValue);
  106. //        }
  107.         setModified(false);
  108.         setValidState(UNVALIDATED);
  109.         // JW: initial check to force visual feedback on required
  110.         isValid();
  111.         pulling = false;
  112.         return true;
  113.     }
  114.     public boolean push() {
  115.         if (isValid()) {
  116.             pushing = true;
  117.             dataModel.setValue(metaData.getName(), cachedValue);
  118.             setModified(false);
  119.             pushing = false;
  120.             return true;
  121.         }
  122.         return false;
  123.     }
  124.     
  125.     public boolean isModified() {
  126.         return modified;
  127.     }
  128.     public boolean isValid() {
  129.         if (validState != UNVALIDATED) {
  130.             return validState == VALID;
  131.         }
  132.         // need to validate
  133.         clearValidationErrors();
  134.         Object componentValue = getComponentValue();
  135.         // step 1: ensure a required element has non-null value
  136.         boolean ok = checkRequired(componentValue);
  137.         // step 2: if necessary, convert value from component to data type
  138.         //         appropriate for model
  139.         Object convertedValue = null;
  140.         if (ok) {
  141.             try {
  142.                 convertedValue = convertToModelType(componentValue);
  143.             } catch (Exception e) {
  144.                 ok = false;
  145.                 /**@todo aim: very nerdy message */
  146.                 addError("value must be type " + metaData.getElementClass().getName());
  147.             }
  148.         }
  149.         // step 3: run any registered element-level validators
  150.         if (ok) {
  151.             ok = executeValidators(convertedValue);
  152.         }
  153.         if (ok) {
  154.             cachedValue = convertedValue;
  155.         }
  156.         setValidState(ok? VALID : INVALID);
  157.         return validState == VALID;
  158.     }
  159.     public int getValidState() {
  160.         return validState;
  161.     }
  162.     public String[] getValidationErrors() {
  163.         if (errorList != null) {
  164.             return (String[])errorList.toArray(new String[1]);
  165.         }
  166.         return new String[0];
  167.     }
  168.     public void clearValidationErrors() {
  169.         if (errorList != null) {
  170.             errorList.clear();
  171.         }
  172.     }
  173.     /**
  174.      * Adds the specified property change listener to this binding object.
  175.      * @param pcl PropertyChangeListener object to receive events when binding
  176.      *        properties change
  177.      */
  178.     public void addPropertyChangeListener(PropertyChangeListener pcl) {
  179.         if (pcs == null) {
  180.             pcs = new PropertyChangeSupport(this);
  181.         }
  182.         pcs.addPropertyChangeListener(pcl);
  183.     }
  184.     /**
  185.      * Removes the specified property change listener from this binding object.
  186.      * @param pcl PropertyChangeListener object to receive events when binding
  187.      *        properties change
  188.      */
  189.     public void removePropertyChangeListener(PropertyChangeListener pcl) {
  190.         pcs.removePropertyChangeListener(pcl);
  191.     }
  192.     /**
  193.      *
  194.      * @return array containing the PropertyChangeListener objects registered
  195.      *         on this binding object
  196.      */
  197.     public PropertyChangeListener[] getPropertyChangeListeners() {
  198.         if (pcs == null) return EMPTY_PROPERTY_CHANGE_LISTENER_ARRAY;
  199.         return pcs.getPropertyChangeListeners();
  200.     }
  201.     protected void firePropertyChange(String propertyName,
  202.                                       Object oldValue, Object newValue) {
  203.         if (pcs == null) return;
  204.         pcs.firePropertyChange(propertyName, oldValue, newValue);
  205.     }
  206.     
  207. //----------------- component related access to implement by subclasses
  208.     
  209.     /**
  210.      * set component and configures metaData dependent logic/constraint state.
  211.      * @param component
  212.      */
  213.     protected abstract void setComponent(JComponent component);
  214.     protected abstract Object getComponentValue();
  215.     protected abstract void setComponentValue(Object value);
  216. //---------------------- update internal state: conversion/validation
  217.     
  218.     protected boolean checkRequired(Object componentValue) {
  219. //        if (metaData != null) {
  220.             if (metaData.isRequired() && isEmpty(componentValue)) {
  221.                 addError("requires a value");
  222.                 return false;
  223.             }
  224.             return true;
  225. //        }
  226. //        return false;
  227.     }
  228.     protected boolean isEmpty(Object componentValue) {
  229.         return (componentValue == null ||
  230.          (componentValue instanceof String && ((String)componentValue).equals("")));
  231.     }
  232.     protected Object convertToModelType(Object componentValue) throws ConversionException {
  233.         Object convertedValue = null;
  234.         // if the element is not required and the value is null, then it's
  235.         // okay to skip conversion
  236.         if (isEmpty(componentValue)) {
  237.             return convertedValue;
  238.         }
  239.         Class elementClass = metaData.getElementClass();
  240.         if (componentValue instanceof String) {
  241.             String stringValue = (String) componentValue;
  242.             Converter converter = metaData.getConverter();
  243.             if (converter != null) {
  244.                 convertedValue = converter.decode(stringValue,
  245.                                                       metaData.getDecodeFormat());
  246.             } else if (metaData.getElementClass() == String.class) {
  247.                 convertedValue = componentValue;
  248.             }
  249.         }
  250.         else {
  251.             if (!elementClass.isAssignableFrom(componentValue.getClass())) {
  252.                 throw new ConversionException("cannot assign component value");
  253.             } else {
  254.                 convertedValue = componentValue;
  255.             }
  256.         }
  257.         return convertedValue;
  258.     }
  259.     protected String convertFromModelType(Object modelValue) {
  260.         if (modelValue != null) {
  261.             try {
  262.                 Converter converter = metaData.getConverter();
  263.                 return converter.encode(modelValue, metaData.getEncodeFormat());
  264.             }
  265.             catch (Exception e) {
  266.                 /**@todo aim: how to handle conversion failure? */
  267.                 return modelValue.toString();
  268.             }
  269.         }
  270.         return "";
  271.     }
  272.     protected boolean executeValidators(Object value) {
  273.         Validator validators[] = metaData.getValidators();
  274.         boolean isValid = true;
  275.         for (int i = 0; i < validators.length; i++) {
  276.             String error[] = new String[1];
  277.             boolean passed = validators[i].validate(value, null, error);
  278.             if (!passed) {
  279.                 String errorMessage = error[0];
  280.                 if (errorMessage != null) {
  281.                     addError(errorMessage);
  282.                 }
  283.                 isValid = false;
  284.             }
  285.         }
  286.         return isValid;
  287.     }
  288.     protected void addError(String error) {
  289.         if (errorList == null) {
  290.             errorList = new ArrayList();
  291.         }
  292.         errorList.add(error);
  293.     }
  294.     protected void setModified(boolean modified) {
  295.         // JW: commented to fix issue #78
  296. //        if (pulling) {
  297. //            return;
  298. //        }
  299.         boolean oldModified = this.modified;
  300.         this.modified = modified;
  301.         if (modified) {
  302.             cachedValue = null;
  303.             setValidState(UNVALIDATED);
  304.         }
  305.         if (oldModified != modified) {
  306.             firePropertyChange("modified",
  307.                                 Boolean.valueOf(oldModified),
  308.                                 Boolean.valueOf(modified));
  309.         }
  310.     }
  311.     protected void setValidState(int validState) {
  312.         int oldValidState = this.validState;
  313.         this.validState = validState;
  314.         if (oldValidState != validState &&
  315.             validState == UNVALIDATED) {
  316.             clearValidationErrors();
  317.         }
  318.         if (validState != oldValidState) {
  319.             firePropertyChange("validState",
  320.                                new Integer(oldValidState),
  321.                                new Integer(validState));
  322.         }
  323.     }
  324.     
  325.     /** installs an InputVerifier depending on 
  326.      *  validationPolicy. 
  327.      */
  328.     protected void installInputVerifier() {
  329.         getComponent().setInputVerifier(new InputVerifier() {
  330.             public boolean verify(JComponent input) {
  331.                 if (validationPolicy != AUTO_VALIDATE_NONE) {
  332.                     boolean isValid = isValid();
  333.                     if (!isValid && validationPolicy == AUTO_VALIDATE_STRICT) {
  334.                         return false;
  335.                     }
  336.                     return true;
  337.                 }
  338.                 return true;
  339.             }
  340.         });
  341.     }
  342. //---------------------- init models, listeners
  343.     
  344.     protected void checkNull(Object component, String message) {
  345.         if (component == null) throw new NullPointerException(message);
  346.     }
  347.     protected void installDataModel(DataModel dataModel, String fieldName) {
  348.         this.dataModel = dataModel;
  349.         this.fieldName = fieldName;
  350.         metaData = dataModel.getMetaData(fieldName);
  351.         // JW, Noel Grandin: the idea is to fail fast on typos
  352.         // should be moved to dataModel api
  353.         // JW: DirectTableBinding will fail
  354.         // JW: for now do it anyway - let DirectTableBinding work it out
  355.         checkNull(metaData, "Field " + fieldName
  356.                 + " does not exist in metadata");
  357.         installDataModelListener();
  358.         installMetaDataListener();
  359.     }
  360.     protected void installDataModelListener() {
  361.         dataModel.addValueChangeListener(new ValueChangeListener() {
  362.             public void valueChanged(ValueChangeEvent e) {
  363.                 if (e.getFieldName().equals(fieldName) &&
  364.                       !pushing) {
  365.                     pull();
  366.                 }
  367.             }
  368.         });
  369.     }
  370.     /**
  371.      * 
  372.      * here: does nothing
  373.      *
  374.      */
  375.     protected void installMetaDataListener() {
  376. //        metaData.addPropertyChangeListener(new PropertyChangeListener() {
  377. //
  378. //            public void propertyChange(PropertyChangeEvent evt) {
  379. //                if (!"enabled".equals(evt.getPropertyName())) return;
  380. //                boolean enabled = ((Boolean) evt.getNewValue()).booleanValue();
  381. //                getComponent().setEnabled(enabled);
  382. //                
  383. //            }
  384. //            
  385. //        });
  386.     }
  387.     
  388. }