    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
    018package org.apache.commons.jxpath.ri.model.beans;
    019
    020import java.util.Locale;
    021
    022import org.apache.commons.jxpath.JXPathBeanInfo;
    023import org.apache.commons.jxpath.JXPathIntrospector;
    024import org.apache.commons.jxpath.ri.QName;
    025import org.apache.commons.jxpath.ri.model.NodePointer;
    026
    027/**
    028 * A Pointer that points to a JavaBean or a collection. It is either the first element of a path or a pointer for a property value. Typically there is a
    029 * {@link BeanPropertyPointer} between two BeanPointers in the chain.
    030 */
    031public class BeanPointer extends PropertyOwnerPointer {
    032
    033    private static final long serialVersionUID = -8227317938284982440L;
    034
    035    /**
    036     * Qualified name.
    037     */
    038    private final QName qName;
    039
    040    /**
    041     * Bean.
    042     */
    043    private final Object bean;
    044
    045    /**
    046     * Bean info.
    047     */
    048    private final JXPathBeanInfo beanInfo;
    049
    050    /**
    051     * BeanPointer a new BeanPointer.
    052     *
    053     * @param parent   pointer
    054     * @param qName     is the name given to the first node
    055     * @param bean     pointed
    056     * @param beanInfo JXPathBeanInfo
    057     */
    058    public BeanPointer(final NodePointer parent, final QName qName, final Object bean, final JXPathBeanInfo beanInfo) {
    059        super(parent);
    060        this.qName = qName;
    061        this.bean = bean;
    062        this.beanInfo = beanInfo;
    063    }
    064
    065    /**
    066     * Constructs a new BeanPointer.
    067     *
    068     * @param qName     is the name given to the first node
    069     * @param bean     pointed
    070     * @param beanInfo JXPathBeanInfo
    071     * @param locale   Locale
    072     */
    073    public BeanPointer(final QName qName, final Object bean, final JXPathBeanInfo beanInfo, final Locale locale) {
    074        super(null, locale);
    075        this.qName = qName;
    076        this.bean = bean;
    077        this.beanInfo = beanInfo;
    078    }
    079
    080    /**
    081     * {@inheritDoc} If the pointer has a parent, then parent's path. If the bean is null, "null()". If the bean is a primitive value, the value itself.
    082     * Otherwise - an empty string.
    083     */
    084    @Override
    085    public String asPath() {
    086        if (parent != null) {
    087            return super.asPath();
    088        }
    089        if (bean == null) {
    090            return "null()";
    091        }
    092        if (bean instanceof Number) {
    093            String string = bean.toString();
    094            if (string.endsWith(".0")) {
    095                string = string.substring(0, string.length() - 2);
    096            }
    097            return string;
    098        }
    099        if (bean instanceof Boolean) {
    100            return ((Boolean) bean).booleanValue() ? "true()" : "false()";
    101        }
    102        if (bean instanceof String) {
    103            return "'" + bean + "'";
    104        }
    105        return "/";
    106    }
    107
    108    @Override
    109    public boolean equals(final Object object) {
    110        if (object == this) {
    111            return true;
    112        }
    113        if (!(object instanceof BeanPointer)) {
    114            return false;
    115        }
    116        final BeanPointer other = (BeanPointer) object;
    117        if (parent != other.parent && (parent == null || !parent.equals(other.parent))) {
    118            return false;
    119        }
    120        if (qName == null && other.qName != null || qName != null && !qName.equals(other.qName)) {
    121            return false;
    122        }
    123        final int iThis = index == WHOLE_COLLECTION ? 0 : index;
    124        final int iOther = other.index == WHOLE_COLLECTION ? 0 : other.index;
    125        if (iThis != iOther) {
    126            return false;
    127        }
    128        if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) {
    129            return bean.equals(other.bean);
    130        }
    131        return bean == other.bean;
    132    }
    133
    134    @Override
    135    public Object getBaseValue() {
    136        return bean;
    137    }
    138
    139    /**
    140     * {@inheritDoc}
    141     *
    142     * @return 1
    143     */
    144    @Override
    145    public int getLength() {
    146        return 1;
    147    }
    148
    149    @Override
    150    public QName getName() {
    151        return qName;
    152    }
    153
    154    @Override
    155    public PropertyPointer getPropertyPointer() {
    156        return new BeanPropertyPointer(this, beanInfo);
    157    }
    158
    159    @Override
    160    public int hashCode() {
    161        return qName == null ? 0 : qName.hashCode();
    162    }
    163
    164    /**
    165     * {@inheritDoc}
    166     *
    167     * @return false
    168     */
    169    @Override
    170    public boolean isCollection() {
    171        return false;
    172    }
    173
    174    @Override
    175    public boolean isLeaf() {
    176        final Object value = getNode();
    177        return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
    178    }
    179}