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}