    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.ri.model.dynabeans;
    17
    18  import java.util.Arrays;
    19
    20  import org.apache.commons.beanutils.DynaBean;
    21  import org.apache.commons.beanutils.DynaClass;
    22  import org.apache.commons.beanutils.DynaProperty;
    23  import org.apache.commons.jxpath.JXPathException;
    24  import org.apache.commons.jxpath.ri.model.NodePointer;
    25  import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
    26  import org.apache.commons.jxpath.util.TypeUtils;
    27  import org.apache.commons.jxpath.util.ValueUtils;
    28
    29  /***
    30   * Pointer pointing to a property of a DynaBean.
    31   *
    32   * @author Dmitri Plotnikov
    33   * @version $Revision: 1.12 $ $Date: 2004/04/04 22:06:35 $
    34   */
    35  public class DynaBeanPropertyPointer extends PropertyPointer {
    36      private DynaBean dynaBean;
    37      private String name;
    38      private String[] names;
    39
    40      public DynaBeanPropertyPointer(NodePointer parent, DynaBean dynaBean) {
    41          super(parent);
    42          this.dynaBean = dynaBean;
    43      }
    44
    45      public Object getBaseValue() {
    46          return dynaBean.get(getPropertyName());
    47      }
    48
    49      /***
    50       * This type of node is auxiliary.
    51       */
    52      public boolean isContainer() {
    53          return true;
    54      }
    55
    56      /***
    57       * Number of the DP object's properties.
    58       */
    59      public int getPropertyCount() {
    60          return getPropertyNames().length;
    61      }
    62
    63      /***
    64       * Names of all properties, sorted alphabetically
    65       *
    66       * @todo do something about the sorting
    67       */
    68      public String[] getPropertyNames() {
    69          if (names == null) {
    70              DynaClass dynaClass = dynaBean.getDynaClass();
    71              DynaProperty properties[] = dynaClass.getDynaProperties();
    72              int count = properties.length;
    73              boolean hasClass = dynaClass.getDynaProperty("class") != null;
    74              if (hasClass) {
    75                  count--;       // Exclude "class" from properties
    76              }
    77              names = new String[count];
    78              for (int i = 0, j = 0; i < properties.length; i++) {
    79                  String name = properties[i].getName();
    80                  if (!hasClass || !name.equals("class")) {
    81                      names[j++] = name;
    82                  }
    83              }
    84              Arrays.sort(names);
    85          }
    86          return names;
    87      }
    88
    89      /***
    90       * Returns the name of the currently selected property or "*"
    91       * if none has been selected.
    92       */
    93      public String getPropertyName() {
    94          if (name == null) {
    95              String names[] = getPropertyNames();
    96              if (propertyIndex >= 0 && propertyIndex < names.length) {
    97                  name = names[propertyIndex];
    98              }
    99              else {
    100                 name = "*";
    101             }
    102         }
    103         return name;
    104     }
    105
    106     /***
    107      * Select a property by name.
    108      */
    109     public void setPropertyName(String propertyName) {
    110         setPropertyIndex(UNSPECIFIED_PROPERTY);
    111         this.name = propertyName;
    112     }
    113
    114     /***
    115      * Index of the currently selected property in the list of all
    116      * properties sorted alphabetically.
    117      */
    118     public int getPropertyIndex() {
    119         if (propertyIndex == UNSPECIFIED_PROPERTY) {
    120             String names[] = getPropertyNames();
    121             for (int i = 0; i < names.length; i++) {
    122                 if (names[i].equals(name)) {
    123                     propertyIndex = i;
    124                     name = null;
    125                     break;
    126                 }
    127             }
    128         }
    129         return super.getPropertyIndex();
    130     }
    131
    132     /***
    133      * Index a property by its index in the list of all
    134      * properties sorted alphabetically.
    135      */
    136     public void setPropertyIndex(int index) {
    137         if (propertyIndex != index) {
    138             super.setPropertyIndex(index);
    139             name = null;
    140         }
    141     }
    142
    143     /***
    144      * If index == WHOLE_COLLECTION, the value of the property, otherwise
    145      * the value of the index'th element of the collection represented by the
    146      * property. If the property is not a collection, index should be zero
    147      * and the value will be the property itself.
    148      */
    149     public Object getImmediateNode() {
    150         String name = getPropertyName();
    151         if (name.equals("*")) {
    152             return null;
    153         }
    154
    155         Object value;
    156         if (index == WHOLE_COLLECTION) {
    157             value = ValueUtils.getValue(dynaBean.get(name));
    158         }
    159         else if (isIndexedProperty()) {
    160             // DynaClass at this point is not based on whether
    161             // the property is indeed indexed, but rather on
    162             // whether it is an array or List. Therefore
    163             // the indexed set may fail.
    164             try {
    165                 value = ValueUtils.getValue(dynaBean.get(name, index));
    166             }
    167             catch (ArrayIndexOutOfBoundsException ex) {
    168                 value = null;
    169             }
    170             catch (IllegalArgumentException ex) {
    171                 value = dynaBean.get(name);
    172                 value = ValueUtils.getValue(value, index);
    173             }
    174         }
    175         else {
    176             value = dynaBean.get(name);
    177             if (ValueUtils.isCollection(value)) {
    178                 value = ValueUtils.getValue(value, index);
    179             }
    180             else if (index != 0) {
    181                 value = null;
    182             }
    183         }
    184         return value;
    185     }
    186
    187     /***
    188      * Returns true if the bean has the currently selected property
    189      */
    190     protected boolean isActualProperty() {
    191         DynaClass dynaClass = dynaBean.getDynaClass();
    192         return dynaClass.getDynaProperty(getPropertyName()) != null;
    193     }
    194
    195     protected boolean isIndexedProperty() {
    196         DynaClass dynaClass = dynaBean.getDynaClass();
    197         DynaProperty property = dynaClass.getDynaProperty(name);
    198         return property.isIndexed();
    199     }
    200
    201     /***
    202      * If index == WHOLE_COLLECTION, change the value of the property, otherwise
    203      * change the value of the index'th element of the collection
    204      * represented by the property.
    205      */
    206     public void setValue(Object value) {
    207         setValue(index, value);
    208     }
    209
    210     public void remove() {
    211         if (index == WHOLE_COLLECTION) {
    212             dynaBean.set(getPropertyName(), null);
    213         }
    214         else if (isIndexedProperty()) {
    215             dynaBean.set(getPropertyName(), index, null);
    216         }
    217         else if (isCollection()) {
    218             Object collection = ValueUtils.remove(getBaseValue(), index);
    219             dynaBean.set(getPropertyName(), collection);
    220         }
    221         else if (index == 0) {
    222             dynaBean.set(getPropertyName(), null);
    223         }
    224     }
    225
    226     private void setValue(int index, Object value) {
    227         if (index == WHOLE_COLLECTION) {
    228             dynaBean.set(getPropertyName(), convert(value, false));
    229         }
    230         else if (isIndexedProperty()) {
    231             dynaBean.set(getPropertyName(), index, convert(value, true));
    232         }
    233         else {
    234             Object baseValue = dynaBean.get(getPropertyName());
    235             ValueUtils.setValue(baseValue, index, value);
    236         }
    237    }
    238
    239
    240     private Object convert(Object value, boolean element) {
    241         DynaClass dynaClass = (DynaClass) dynaBean.getDynaClass();
    242         DynaProperty property = dynaClass.getDynaProperty(getPropertyName());
    243         Class type = property.getType();
    244         if (element) {
    245             if (type.isArray()) {
    246                 type = type.getComponentType();
    247             }
    248             else {
    249                 return value; // No need to convert
    250             }
    251         }
    252
    253         try {
    254             return TypeUtils.convert(value, type);
    255         }
    256         catch (Exception ex) {
    257             throw new JXPathException(
    258                 "Cannot convert value of class "
    259                     + (value == null ? "null" : value.getClass().getName())
    260                     + " to type "
    261                     + type,
    262                 ex);
    263         }
    264     }
    265 }