    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath.ri.model.beans;
    018
    019    import org.apache.commons.jxpath.JXPathException;
    020    import org.apache.commons.jxpath.ri.model.NodeIterator;
    021    import org.apache.commons.jxpath.ri.model.NodePointer;
    022
    023    /**
    024     * Iterates property values of an object pointed at with a {@link PropertyOwnerPointer}.
    025     * Examples of such objects are JavaBeans and objects with Dynamic Properties.
    026     *
    027     * @author Dmitri Plotnikov
    028     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    029     */
    030    public class PropertyIterator implements NodeIterator {
    031        private boolean empty = false;
    032        private boolean reverse;
    033        private String name;
    034        private int startIndex = 0;
    035        private boolean targetReady = false;
    036        private int position = 0;
    037        private PropertyPointer propertyNodePointer;
    038        private int startPropertyIndex;
    039
    040        private boolean includeStart = false;
    041
    042        /**
    043         * Create a new PropertyIterator.
    044         * @param pointer owning pointer
    045         * @param name property name
    046         * @param reverse iteration order
    047         * @param startWith beginning pointer
    048         */
    049        public PropertyIterator(
    050            PropertyOwnerPointer pointer,
    051            String name,
    052            boolean reverse,
    053            NodePointer startWith) {
    054            propertyNodePointer =
    055                (PropertyPointer) pointer.getPropertyPointer().clone();
    056            this.name = name;
    057            this.reverse = reverse;
    058            this.includeStart = true;
    059            if (reverse) {
    060                this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
    061                this.startIndex = -1;
    062            }
    063            if (startWith != null) {
    064                while (startWith != null
    065                        && startWith.getImmediateParentPointer() != pointer) {
    066                    startWith = startWith.getImmediateParentPointer();
    067                }
    068                if (startWith == null) {
    069                    throw new JXPathException(
    070                        "PropertyIerator startWith parameter is "
    071                            + "not a child of the supplied parent");
    072                }
    073                this.startPropertyIndex =
    074                    ((PropertyPointer) startWith).getPropertyIndex();
    075                this.startIndex = startWith.getIndex();
    076                if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
    077                    this.startIndex = 0;
    078                }
    079                this.includeStart = false;
    080                if (reverse && startIndex == -1) {
    081                    this.includeStart = true;
    082                }
    083            }
    084        }
    085
    086        /**
    087         * Get the property pointer.
    088         * @return NodePointer
    089         */
    090        protected NodePointer getPropertyPointer() {
    091            return propertyNodePointer;
    092        }
    093
    094        /**
    095         * Reset property iteration.
    096         */
    097        public void reset() {
    098            position = 0;
    099            targetReady = false;
    100        }
    101
    102        public NodePointer getNodePointer() {
    103            if (position == 0) {
    104                if (name != null) {
    105                    if (!targetReady) {
    106                        prepareForIndividualProperty(name);
    107                    }
    108                    // If there is no such property - return null
    109                    if (empty) {
    110                        return null;
    111                    }
    112                }
    113                else {
    114                    if (!setPosition(1)) {
    115                        return null;
    116                    }
    117                    reset();
    118                }
    119            }
    120            try {
    121                return propertyNodePointer.getValuePointer();
    122            }
    123            catch (Throwable ex) {
    124                // @todo: should this exception be reported in any way?
    125                NullPropertyPointer npp =
    126                    new NullPropertyPointer(
    127                            propertyNodePointer.getImmediateParentPointer());
    128                npp.setPropertyName(propertyNodePointer.getPropertyName());
    129                npp.setIndex(propertyNodePointer.getIndex());
    130                return npp.getValuePointer();
    131            }
    132        }
    133
    134        public int getPosition() {
    135            return position;
    136        }
    137
    138        public boolean setPosition(int position) {
    139            return name == null ? setPositionAllProperties(position) : setPositionIndividualProperty(position);
    140        }
    141
    142        /**
    143         * Set position for an individual property.
    144         * @param position int position
    145         * @return whether this was a valid position
    146         */
    147        private boolean setPositionIndividualProperty(int position) {
    148            this.position = position;
    149            if (position < 1) {
    150                return false;
    151            }
    152
    153            if (!targetReady) {
    154                prepareForIndividualProperty(name);
    155            }
    156
    157            if (empty) {
    158                return false;
    159            }
    160
    161            int length = getLength();
    162            int index;
    163            if (!reverse) {
    164                index = position + startIndex;
    165                if (!includeStart) {
    166                    index++;
    167                }
    168                if (index > length) {
    169                    return false;
    170                }
    171            }
    172            else {
    173                int end = startIndex;
    174                if (end == -1) {
    175                    end = length - 1;
    176                }
    177                index = end - position + 2;
    178                if (!includeStart) {
    179                    index--;
    180                }
    181                if (index < 1) {
    182                    return false;
    183                }
    184            }
    185            propertyNodePointer.setIndex(index - 1);
    186            return true;
    187        }
    188
    189        /**
    190         * Set position for all properties
    191         * @param position int position
    192         * @return whether this was a valid position
    193         */
    194        private boolean setPositionAllProperties(int position) {
    195            this.position = position;
    196            if (position < 1) {
    197                return false;
    198            }
    199
    200            int offset;
    201            int count = propertyNodePointer.getPropertyCount();
    202            if (!reverse) {
    203                int index = 1;
    204                for (int i = startPropertyIndex; i < count; i++) {
    205                    propertyNodePointer.setPropertyIndex(i);
    206                    int length = getLength();
    207                    if (i == startPropertyIndex) {
    208                        length -= startIndex;
    209                        if (!includeStart) {
    210                            length--;
    211                        }
    212                        offset = startIndex + position - index;
    213                        if (!includeStart) {
    214                            offset++;
    215                        }
    216                    }
    217                    else {
    218                        offset = position - index;
    219                    }
    220                    if (index <= position && position < index + length) {
    221                        propertyNodePointer.setIndex(offset);
    222                        return true;
    223                    }
    224                    index += length;
    225                }
    226            }
    227            else {
    228                int index = 1;
    229                int start = startPropertyIndex;
    230                if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
    231                    start = count - 1;
    232                }
    233                for (int i = start; i >= 0; i--) {
    234                    propertyNodePointer.setPropertyIndex(i);
    235                    int length = getLength();
    236                    if (i == startPropertyIndex) {
    237                        int end = startIndex;
    238                        if (end == -1) {
    239                            end = length - 1;
    240                        }
    241                        length = end + 1;
    242                        offset = end - position + 1;
    243                        if (!includeStart) {
    244                            offset--;
    245                            length--;
    246                        }
    247                    }
    248                    else {
    249                        offset = length - (position - index) - 1;
    250                    }
    251
    252                    if (index <= position && position < index + length) {
    253                        propertyNodePointer.setIndex(offset);
    254                        return true;
    255                    }
    256                    index += length;
    257                }
    258            }
    259            return false;
    260        }
    261
    262        /**
    263         * Prepare for an individual property.
    264         * @param name property name
    265         */
    266        protected void prepareForIndividualProperty(String name) {
    267            targetReady = true;
    268            empty = true;
    269
    270            String[] names = propertyNodePointer.getPropertyNames();
    271            if (!reverse) {
    272                if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
    273                    startPropertyIndex = 0;
    274                }
    275                if (startIndex == NodePointer.WHOLE_COLLECTION) {
    276                    startIndex = 0;
    277                }
    278                for (int i = startPropertyIndex; i < names.length; i++) {
    279                    if (names[i].equals(name)) {
    280                        propertyNodePointer.setPropertyIndex(i);
    281                        if (i != startPropertyIndex) {
    282                            startIndex = 0;
    283                            includeStart = true;
    284                        }
    285                        empty = false;
    286                        break;
    287                    }
    288                }
    289            }
    290            else {
    291                if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
    292                    startPropertyIndex = names.length - 1;
    293                }
    294                if (startIndex == NodePointer.WHOLE_COLLECTION) {
    295                    startIndex = -1;
    296                }
    297                for (int i = startPropertyIndex; i >= 0; i--) {
    298                    if (names[i].equals(name)) {
    299                        propertyNodePointer.setPropertyIndex(i);
    300                        if (i != startPropertyIndex) {
    301                            startIndex = -1;
    302                            includeStart = true;
    303                        }
    304                        empty = false;
    305                        break;
    306                    }
    307                }
    308            }
    309        }
    310
    311        /**
    312         * Computes length for the current pointer - ignores any exceptions.
    313         * @return length
    314         */
    315        private int getLength() {
    316            int length;
    317            try {
    318                length = propertyNodePointer.getLength(); // TBD: cache length
    319            }
    320            catch (Throwable t) {
    321                // @todo: should this exception be reported in any way?
    322                length = 0;
    323            }
    324            return length;
    325        }
    326    }