    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 org.apache.commons.jxpath.AbstractFactory;
    19  import org.apache.commons.jxpath.JXPathContext;
    20  import org.apache.commons.jxpath.JXPathException;
    21  import org.apache.commons.jxpath.JXPathIntrospector;
    22  import org.apache.commons.jxpath.ri.QName;
    23  import org.apache.commons.jxpath.ri.model.NodePointer;
    24  import org.apache.commons.jxpath.util.ValueUtils;
    25
    26  /***
    27   * A pointer allocated by a PropertyOwnerPointer to represent the value of
    28   * a property of the parent object.
    29   *
    30   * @author Dmitri Plotnikov
    31   * @version $Revision: 1.14 $ $Date: 2004/04/04 22:06:36 $
    32   */
    33  public abstract class PropertyPointer extends NodePointer {
    34      public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE;
    35
    36      protected int propertyIndex = UNSPECIFIED_PROPERTY;
    37      protected Object bean;
    38
    39      /***
    40       * Takes a javabean, a descriptor of a property of that object and
    41       * an offset within that property (starting with 0).
    42       */
    43      public PropertyPointer(NodePointer parent) {
    44          super(parent);
    45      }
    46
    47      public int getPropertyIndex() {
    48          return propertyIndex;
    49      }
    50
    51      public void setPropertyIndex(int index) {
    52          if (propertyIndex != index) {
    53              propertyIndex = index;
    54              setIndex(WHOLE_COLLECTION);
    55          }
    56      }
    57
    58      public Object getBean() {
    59          if (bean == null) {
    60              bean = getImmediateParentPointer().getNode();
    61          }
    62          return bean;
    63      }
    64
    65      public QName getName() {
    66          return new QName(null, getPropertyName());
    67      }
    68
    69      public abstract String getPropertyName();
    70
    71      public abstract void setPropertyName(String propertyName);
    72
    73      public abstract int getPropertyCount();
    74
    75      public abstract String[] getPropertyNames();
    76
    77      protected abstract boolean isActualProperty();
    78
    79      public boolean isActual() {
    80          if (!isActualProperty()) {
    81              return false;
    82          }
    83
    84          return super.isActual();
    85      }
    86
    87      private static final Object UNINITIALIZED = new Object();
    88
    89      private Object value = UNINITIALIZED;
    90      public Object getImmediateNode() {
    91          if (value == UNINITIALIZED) {
    92              if (index == WHOLE_COLLECTION) {
    93                  value = ValueUtils.getValue(getBaseValue());
    94              }
    95              else {
    96                  value = ValueUtils.getValue(getBaseValue(), index);
    97              }
    98          }
    99          return value;
    100     }
    101
    102     public boolean isCollection() {
    103         Object value = getBaseValue();
    104         return value != null && ValueUtils.isCollection(value);
    105     }
    106
    107     public boolean isLeaf() {
    108         Object value = getNode();
    109         return value == null
    110             || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
    111     }
    112
    113     /***
    114      * If the property contains a collection, then the length of that
    115      * collection, otherwise - 1.
    116      */
    117     public int getLength() {
    118         return ValueUtils.getLength(getBaseValue());
    119     }
    120
    121
    122     /***
    123      * Returns a NodePointer that can be used to access the currently
    124      * selected property value.
    125      */
    126     public NodePointer getImmediateValuePointer() {
    127         return NodePointer.newChildNodePointer(
    128             this,
    129             getName(),
    130             getImmediateNode());
    131     }
    132
    133     public NodePointer createPath(JXPathContext context) {
    134         if (getImmediateNode() == null) {
    135             AbstractFactory factory = getAbstractFactory(context);
    136             int inx = (index == WHOLE_COLLECTION ? 0 : index);
    137             boolean success =
    138                 factory.createObject(
    139                     context,
    140                     this,
    141                     getBean(),
    142                     getPropertyName(),
    143                     inx);
    144             if (!success) {
    145                 throw new JXPathException(
    146                     "Factory "
    147                         + factory
    148                         + " could not create an object for path: "
    149                         + asPath());
    150             }
    151         }
    152         return this;
    153     }
    154
    155     public NodePointer createPath(JXPathContext context, Object value) {
    156         // If neccessary, expand collection
    157         if (index != WHOLE_COLLECTION && index >= getLength()) {
    158             createPath(context);
    159         }
    160         setValue(value);
    161         return this;
    162     }
    163
    164     public NodePointer createChild(
    165         JXPathContext context,
    166         QName name,
    167         int index,
    168         Object value)
    169     {
    170         PropertyPointer prop = (PropertyPointer) clone();
    171         if (name != null) {
    172             prop.setPropertyName(name.toString());
    173         }
    174         prop.setIndex(index);
    175         return prop.createPath(context, value);
    176     }
    177
    178     public NodePointer createChild(
    179         JXPathContext context,
    180         QName name,
    181         int index)
    182     {
    183         PropertyPointer prop = (PropertyPointer) clone();
    184         if (name != null) {
    185             prop.setPropertyName(name.toString());
    186         }
    187         prop.setIndex(index);
    188         return prop.createPath(context);
    189     }
    190
    191     public int hashCode() {
    192         return getImmediateParentPointer().hashCode() + propertyIndex + index;
    193     }
    194
    195     public boolean equals(Object object) {
    196         if (object == this) {
    197             return true;
    198         }
    199
    200         if (!(object instanceof PropertyPointer)) {
    201             return false;
    202         }
    203
    204         PropertyPointer other = (PropertyPointer) object;
    205         if (parent != other.parent) {
    206             if (parent == null || !parent.equals(other.parent)) {
    207                 return false;
    208             }
    209         }
    210
    211         if (getPropertyIndex() != other.getPropertyIndex()
    212             || !getPropertyName().equals(other.getPropertyName())) {
    213             return false;
    214         }
    215
    216         int iThis = (index == WHOLE_COLLECTION ? 0 : index);
    217         int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index);
    218         return iThis == iOther;
    219     }
    220
    221     public int compareChildNodePointers(
    222         NodePointer pointer1,
    223         NodePointer pointer2)
    224     {
    225         return getValuePointer().compareChildNodePointers(pointer1, pointer2);
    226     }
    227
    228     private AbstractFactory getAbstractFactory(JXPathContext context) {
    229         AbstractFactory factory = context.getFactory();
    230         if (factory == null) {
    231             throw new JXPathException(
    232                 "Factory is not set on the "
    233                     + "JXPathContext - cannot create path: "
    234                     + asPath());
    235         }
    236         return factory;
    237     }
    238 }