    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.JXPathException;
    19  import org.apache.commons.jxpath.ri.model.NodeIterator;
    20  import org.apache.commons.jxpath.ri.model.NodePointer;
    21
    22  /***
    23   * Iterates property values of an object pointed at with a PropertyOwnerPointer.
    24   * Examples of such objects are JavaBeans and objects with Dynamic Properties.
    25   *
    26   * @author Dmitri Plotnikov
    27   * @version $Revision: 1.13 $ $Date: 2004/03/25 03:49:50 $
    28   */
    29  public class PropertyIterator implements NodeIterator {
    30      private boolean empty = false;
    31      private boolean reverse;
    32      private String name;
    33      private int startIndex = 0;
    34      private boolean targetReady = false;
    35      private int position = 0;
    36      private PropertyPointer propertyNodePointer;
    37      private int startPropertyIndex;
    38
    39      private boolean ready = false;
    40      private boolean includeStart = false;
    41
    42      public PropertyIterator(
    43          PropertyOwnerPointer pointer,
    44          String name,
    45          boolean reverse,
    46          NodePointer startWith)
    47      {
    48          propertyNodePointer =
    49              (PropertyPointer) pointer.getPropertyPointer().clone();
    50          this.name = name;
    51          this.reverse = reverse;
    52          this.includeStart = true;
    53          if (reverse) {
    54              this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
    55              this.startIndex = -1;
    56          }
    57          if (startWith != null) {
    58              while (startWith != null
    59                      && startWith.getImmediateParentPointer() != pointer) {
    60                  startWith = startWith.getImmediateParentPointer();
    61              }
    62              if (startWith == null) {
    63                  throw new JXPathException(
    64                      "PropertyIerator startWith parameter is "
    65                          + "not a child of the supplied parent");
    66              }
    67              this.startPropertyIndex =
    68                  ((PropertyPointer) startWith).getPropertyIndex();
    69              this.startIndex = startWith.getIndex();
    70              if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
    71                  this.startIndex = 0;
    72              }
    73              this.includeStart = false;
    74              if (reverse && startIndex == -1) {
    75                  this.includeStart = true;
    76              }
    77          }
    78      }
    79
    80      protected NodePointer getPropertyPointer() {
    81          return propertyNodePointer;
    82      }
    83
    84      public void reset() {
    85          position = 0;
    86          targetReady = false;
    87      }
    88
    89      public NodePointer getNodePointer() {
    90          if (position == 0) {
    91              if (name != null) {
    92                  if (!targetReady) {
    93                      prepareForIndividualProperty(name);
    94                  }
    95                  // If there is no such property - return null
    96                  if (empty) {
    97                      return null;
    98                  }
    99              }
    100             else {
    101                 if (!setPosition(1)) {
    102                     return null;
    103                 }
    104                 reset();
    105             }
    106         }
    107         try {
    108             return propertyNodePointer.getValuePointer();
    109         }
    110         catch (Throwable ex) {
    111             // @todo: should this exception be reported in any way?
    112             NullPropertyPointer npp =
    113                 new NullPropertyPointer(
    114                         propertyNodePointer.getImmediateParentPointer());
    115             npp.setPropertyName(propertyNodePointer.getPropertyName());
    116             npp.setIndex(propertyNodePointer.getIndex());
    117             return npp.getValuePointer();
    118         }
    119     }
    120
    121     public int getPosition() {
    122         return position;
    123     }
    124
    125     public boolean setPosition(int position) {
    126         if (name != null) {
    127             return setPositionIndividualProperty(position);
    128         }
    129         else {
    130             return setPositionAllProperties(position);
    131         }
    132     }
    133
    134     private boolean setPositionIndividualProperty(int position) {
    135         this.position = position;
    136         if (position < 1) {
    137             return false;
    138         }
    139
    140         if (!targetReady) {
    141             prepareForIndividualProperty(name);
    142         }
    143
    144         if (empty) {
    145             return false;
    146         }
    147
    148         int length = getLength();
    149         int index;
    150         if (!reverse) {
    151             index = position + startIndex;
    152             if (!includeStart) {
    153                 index++;
    154             }
    155             if (index > length) {
    156                 return false;
    157             }
    158         }
    159         else {
    160             int end = startIndex;
    161             if (end == -1) {
    162                 end = length - 1;
    163             }
    164             index = end - position + 2;
    165             if (!includeStart) {
    166                 index--;
    167             }
    168             if (index < 1) {
    169                 return false;
    170             }
    171         }
    172         propertyNodePointer.setIndex(index - 1);
    173         return true;
    174     }
    175
    176     private boolean setPositionAllProperties(int position) {
    177         this.position = position;
    178         if (position < 1) {
    179             return false;
    180         }
    181
    182         int offset;
    183         int count = propertyNodePointer.getPropertyCount();
    184         if (!reverse) {
    185             int index = 1;
    186             for (int i = startPropertyIndex; i < count; i++) {
    187                 propertyNodePointer.setPropertyIndex(i);
    188                 int length = getLength();
    189                 if (i == startPropertyIndex) {
    190                     length -= startIndex;
    191                     if (!includeStart) {
    192                         length--;
    193                     }
    194                     offset = startIndex + position - index;
    195                     if (!includeStart) {
    196                         offset++;
    197                     }
    198                 }
    199                 else {
    200                     offset = position - index;
    201                 }
    202                 if (index <= position && position < index + length) {
    203                     propertyNodePointer.setIndex(offset);
    204                     return true;
    205                 }
    206                 index += length;
    207             }
    208         }
    209         else {
    210             int index = 1;
    211             int start = startPropertyIndex;
    212             if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
    213                 start = count - 1;
    214             }
    215             for (int i = start; i >= 0; i--) {
    216                 propertyNodePointer.setPropertyIndex(i);
    217                 int length = getLength();
    218                 if (i == startPropertyIndex) {
    219                     int end = startIndex;
    220                     if (end == -1) {
    221                         end = length - 1;
    222                     }
    223                     length = end + 1;
    224                     offset = end - position + 1;
    225                     if (!includeStart) {
    226                         offset--;
    227                         length--;
    228                     }
    229                 }
    230                 else {
    231                     offset = length - (position - index) - 1;
    232                 }
    233
    234                 if (index <= position && position < index + length) {
    235                     propertyNodePointer.setIndex(offset);
    236                     return true;
    237                 }
    238                 index += length;
    239             }
    240         }
    241         return false;
    242     }
    243
    244     protected void prepareForIndividualProperty(String name) {
    245         targetReady = true;
    246         empty = true;
    247
    248         String names[] = propertyNodePointer.getPropertyNames();
    249         if (!reverse) {
    250             if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
    251                 startPropertyIndex = 0;
    252             }
    253             if (startIndex == NodePointer.WHOLE_COLLECTION) {
    254                 startIndex = 0;
    255             }
    256             for (int i = startPropertyIndex; i < names.length; i++) {
    257                 if (names[i].equals(name)) {
    258                     propertyNodePointer.setPropertyIndex(i);
    259                     if (i != startPropertyIndex) {
    260                         startIndex = 0;
    261                         includeStart = true;
    262                     }
    263                     empty = false;
    264                     break;
    265                 }
    266             }
    267         }
    268         else {
    269             if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
    270                 startPropertyIndex = names.length - 1;
    271             }
    272             if (startIndex == NodePointer.WHOLE_COLLECTION) {
    273                 startIndex = -1;
    274             }
    275             for (int i = startPropertyIndex; i >= 0; i--) {
    276                 if (names[i].equals(name)) {
    277                     propertyNodePointer.setPropertyIndex(i);
    278                     if (i != startPropertyIndex) {
    279                         startIndex = -1;
    280                         includeStart = true;
    281                     }
    282                     empty = false;
    283                     break;
    284                 }
    285             }
    286         }
    287     }
    288
    289     /***
    290      * Computes length for the current pointer - ignores any exceptions
    291      */
    292     private int getLength() {
    293         int length;
    294         try {
    295             length = propertyNodePointer.getLength(); // TBD: cache length
    296         }
    297         catch (Throwable t) {
    298             // @todo: should this exception be reported in any way?
    299             length = 0;
    300         }
    301         return length;
    302     }
    303 }