    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.beans;
    17
    18  import java.beans.IndexedPropertyDescriptor;
    19  import java.beans.PropertyDescriptor;
    20
    21  import org.apache.commons.jxpath.JXPathBeanInfo;
    22  import org.apache.commons.jxpath.JXPathContext;
    23  import org.apache.commons.jxpath.JXPathException;
    24  import org.apache.commons.jxpath.ri.model.NodePointer;
    25  import org.apache.commons.jxpath.util.ValueUtils;
    26
    27  /***
    28   * Pointer pointing to a property of a JavaBean.
    29   *
    30   * @author Dmitri Plotnikov
    31   * @version $Revision: 1.17 $ $Date: 2004/04/04 22:06:36 $
    32   */
    33  public class BeanPropertyPointer extends PropertyPointer {
    34      private String propertyName;
    35      private JXPathBeanInfo beanInfo;
    36      private PropertyDescriptor propertyDescriptors[];
    37      private PropertyDescriptor propertyDescriptor;
    38      private String[] names;
    39      private static final Object UNINITIALIZED = new Object();
    40      private Object baseValue = UNINITIALIZED;
    41      private Object value = UNINITIALIZED;
    42
    43      private static final int UNKNOWN_LENGTH_MAX_COUNT = 10000;
    44
    45      public BeanPropertyPointer(NodePointer parent, JXPathBeanInfo beanInfo) {
    46          super(parent);
    47          this.beanInfo = beanInfo;
    48      }
    49
    50      /***
    51       * This type of node is auxiliary.
    52       */
    53      public boolean isContainer() {
    54          return true;
    55      }
    56
    57      /***
    58       * Number of the bean's properties.
    59       */
    60      public int getPropertyCount() {
    61          return getPropertyDescriptors().length;
    62      }
    63
    64      /***
    65       * Names of all properties, sorted alphabetically
    66       */
    67      public String[] getPropertyNames() {
    68          if (names == null) {
    69              PropertyDescriptor pds[] = getPropertyDescriptors();
    70              names = new String[pds.length];
    71              for (int i = 0; i < names.length; i++) {
    72                  names[i] = pds[i].getName();
    73              }
    74          }
    75          return names;
    76      }
    77
    78      /***
    79       * Select a property by name
    80       */
    81      public void setPropertyName(String propertyName) {
    82          setPropertyIndex(UNSPECIFIED_PROPERTY);
    83          this.propertyName = propertyName;
    84      }
    85
    86      /***
    87       * Selects a property by its offset in the alphabetically sorted list.
    88       */
    89      public void setPropertyIndex(int index) {
    90          if (propertyIndex != index) {
    91              super.setPropertyIndex(index);
    92              propertyName = null;
    93              propertyDescriptor = null;
    94              baseValue = UNINITIALIZED;
    95              value = UNINITIALIZED;
    96          }
    97      }
    98
    99      /***
    100      * The value of the currently selected property.
    101      */
    102     public Object getBaseValue() {
    103         if (baseValue == UNINITIALIZED) {
    104             PropertyDescriptor pd = getPropertyDescriptor();
    105             if (pd == null) {
    106                 return null;
    107             }
    108             baseValue = ValueUtils.getValue(getBean(), pd);
    109         }
    110         return baseValue;
    111     }
    112
    113     public void setIndex(int index) {
    114         if (this.index != index) {
    115             // When dealing with a scalar, index == 0 is equivalent to
    116             // WHOLE_COLLECTION, so do not change it.
    117             if (this.index != WHOLE_COLLECTION
    118                 || index != 0
    119                 || isCollection()) {
    120                 super.setIndex(index);
    121                 value = UNINITIALIZED;
    122             }
    123         }
    124     }
    125
    126     /***
    127      * If index == WHOLE_COLLECTION, the value of the property, otherwise
    128      * the value of the index'th element of the collection represented by the
    129      * property. If the property is not a collection, index should be zero
    130      * and the value will be the property itself.
    131      */
    132     public Object getImmediateNode() {
    133         if (value == UNINITIALIZED) {
    134             if (index == WHOLE_COLLECTION) {
    135                 value = ValueUtils.getValue(getBaseValue());
    136             }
    137             else {
    138                 PropertyDescriptor pd = getPropertyDescriptor();
    139                 if (pd == null) {
    140                     value = null;
    141                 }
    142                 else {
    143                     value = ValueUtils.getValue(getBean(), pd, index);
    144                 }
    145             }
    146         }
    147         return value;
    148     }
    149
    150     protected boolean isActualProperty() {
    151         return getPropertyDescriptor() != null;
    152     }
    153
    154     public boolean isCollection() {
    155         PropertyDescriptor pd = getPropertyDescriptor();
    156         if (pd == null) {
    157             return false;
    158         }
    159
    160         if (pd instanceof IndexedPropertyDescriptor) {
    161             return true;
    162         }
    163
    164         int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
    165         if (hint == -1) {
    166             return false;
    167         }
    168         if (hint == 1) {
    169             return true;
    170         }
    171
    172         Object value = getBaseValue();
    173         return value != null && ValueUtils.isCollection(value);
    174     }
    175
    176     /***
    177      * If the property contains a collection, then the length of that
    178      * collection, otherwise - 1.
    179      */
    180     public int getLength() {
    181         PropertyDescriptor pd = getPropertyDescriptor();
    182         if (pd == null) {
    183             return 1;
    184         }
    185
    186         if (pd instanceof IndexedPropertyDescriptor) {
    187             return ValueUtils.getIndexedPropertyLength(
    188                 getBean(),
    189                 (IndexedPropertyDescriptor) pd);
    190         }
    191
    192         int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
    193         if (hint == -1) {
    194             return 1;
    195         }
    196         return ValueUtils.getLength(getBaseValue());
    197     }
    198
    199     /***
    200      * If index == WHOLE_COLLECTION, change the value of the property, otherwise
    201      * change the value of the index'th element of the collection
    202      * represented by the property.
    203      */
    204     public void setValue(Object value) {
    205         PropertyDescriptor pd = getPropertyDescriptor();
    206         if (pd == null) {
    207             throw new JXPathException(
    208                 "Cannot set property: " + asPath() + " - no such property");
    209         }
    210
    211         if (index == WHOLE_COLLECTION) {
    212             ValueUtils.setValue(getBean(), pd, value);
    213         }
    214         else {
    215             ValueUtils.setValue(getBean(), pd, index, value);
    216         }
    217         this.value = value;
    218     }
    219
    220     /***
    221      * @see PropertyPointer#createPath(JXPathContext)
    222      */
    223     public NodePointer createPath(JXPathContext context) {
    224         if (getImmediateNode() == null) {
    225             super.createPath(context);
    226             baseValue = UNINITIALIZED;
    227             value = UNINITIALIZED;
    228         }
    229         return this;
    230     }
    231
    232     public void remove() {
    233         if (index == WHOLE_COLLECTION) {
    234             setValue(null);
    235         }
    236         else if (isCollection()) {
    237             Object collection = ValueUtils.remove(getBaseValue(), index);
    238             ValueUtils.setValue(getBean(), getPropertyDescriptor(), collection);
    239         }
    240         else if (index == 0) {
    241             index = WHOLE_COLLECTION;
    242             setValue(null);
    243         }
    244     }
    245
    246     /***
    247      * Name of the currently selected property.
    248      */
    249     public String getPropertyName() {
    250         if (propertyName == null) {
    251             PropertyDescriptor pd = getPropertyDescriptor();
    252             if (pd != null) {
    253                 propertyName = pd.getName();
    254             }
    255         }
    256         return propertyName != null ? propertyName : "*";
    257     }
    258
    259     /***
    260      * Finds the property descriptor corresponding to the current property
    261      * index.
    262      */
    263     private PropertyDescriptor getPropertyDescriptor() {
    264         if (propertyDescriptor == null) {
    265             int inx = getPropertyIndex();
    266             if (inx == UNSPECIFIED_PROPERTY) {
    267                 propertyDescriptor =
    268                     beanInfo.getPropertyDescriptor(propertyName);
    269             }
    270             else {
    271                 PropertyDescriptor propertyDescriptors[] =
    272                     getPropertyDescriptors();
    273                 if (inx >= 0 && inx < propertyDescriptors.length) {
    274                     propertyDescriptor = propertyDescriptors[inx];
    275                 }
    276                 else {
    277                     propertyDescriptor = null;
    278                 }
    279             }
    280         }
    281         return propertyDescriptor;
    282     }
    283
    284     protected PropertyDescriptor[] getPropertyDescriptors() {
    285         if (propertyDescriptors == null) {
    286             propertyDescriptors = beanInfo.getPropertyDescriptors();
    287         }
    288         return propertyDescriptors;
    289     }
    290 }