/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.jxpath.ri.model.dynabeans; import java.util.Arrays; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.DynaClass; import org.apache.commons.beanutils.DynaProperty; import org.apache.commons.jxpath.JXPathTypeConversionException; import org.apache.commons.jxpath.ri.model.NodePointer; import org.apache.commons.jxpath.ri.model.beans.PropertyPointer; import org.apache.commons.jxpath.util.TypeUtils; import org.apache.commons.jxpath.util.ValueUtils; /** * Pointer pointing to a property of a {@link DynaBean}. If the target DynaBean is * Serializable, so should this instance be. * * @author Dmitri Plotnikov * @version $Revision: 668329 $ $Date: 2008-06-16 16:59:48 -0500 (Mon, 16 Jun 2008) $ */ public class DynaBeanPropertyPointer extends PropertyPointer { private DynaBean dynaBean; private String name; private String[] names; private static final long serialVersionUID = 2094421509141267239L; /** * Create a new DynaBeanPropertyPointer. * @param parent pointer * @param dynaBean pointed */ public DynaBeanPropertyPointer(NodePointer parent, DynaBean dynaBean) { super(parent); this.dynaBean = dynaBean; } public Object getBaseValue() { return dynaBean.get(getPropertyName()); } /** * This type of node is auxiliary. * @return true */ public boolean isContainer() { return true; } public int getPropertyCount() { return getPropertyNames().length; } public String[] getPropertyNames() { /* @todo do something about the sorting - LIKE WHAT? - MJB */ if (names == null) { DynaClass dynaClass = dynaBean.getDynaClass(); DynaProperty[] properties = dynaClass.getDynaProperties(); int count = properties.length; boolean hasClass = dynaClass.getDynaProperty("class") != null; if (hasClass) { count--; // Exclude "class" from properties } names = new String[count]; for (int i = 0, j = 0; i < properties.length; i++) { String name = properties[i].getName(); if (!hasClass || !name.equals("class")) { names[j++] = name; } } Arrays.sort(names); } return names; } /** * Returns the name of the currently selected property or "*" * if none has been selected. * @return String */ public String getPropertyName() { if (name == null) { String[] names = getPropertyNames(); name = propertyIndex >= 0 && propertyIndex < names.length ? names[propertyIndex] : "*"; } return name; } /** * Select a property by name. * @param propertyName to select */ public void setPropertyName(String propertyName) { setPropertyIndex(UNSPECIFIED_PROPERTY); this.name = propertyName; } /** * Index of the currently selected property in the list of all * properties sorted alphabetically. * @return int */ public int getPropertyIndex() { if (propertyIndex == UNSPECIFIED_PROPERTY) { String[] names = getPropertyNames(); for (int i = 0; i < names.length; i++) { if (names[i].equals(name)) { propertyIndex = i; name = null; break; } } } return super.getPropertyIndex(); } /** * Index a property by its index in the list of all * properties sorted alphabetically. * @param index to set */ public void setPropertyIndex(int index) { if (propertyIndex != index) { super.setPropertyIndex(index); name = null; } } /** * If index == WHOLE_COLLECTION, the value of the property, otherwise * the value of the index'th element of the collection represented by the * property. If the property is not a collection, index should be zero * and the value will be the property itself. * @return Object */ public Object getImmediateNode() { String name = getPropertyName(); if (name.equals("*")) { return null; } Object value; if (index == WHOLE_COLLECTION) { value = ValueUtils.getValue(dynaBean.get(name)); } else if (isIndexedProperty()) { // DynaClass at this point is not based on whether // the property is indeed indexed, but rather on // whether it is an array or List. Therefore // the indexed set may fail. try { value = ValueUtils.getValue(dynaBean.get(name, index)); } catch (ArrayIndexOutOfBoundsException ex) { value = null; } catch (IllegalArgumentException ex) { value = dynaBean.get(name); value = ValueUtils.getValue(value, index); } } else { value = dynaBean.get(name); if (ValueUtils.isCollection(value)) { value = ValueUtils.getValue(value, index); } else if (index != 0) { value = null; } } return value; } /** * Returns true if the bean has the currently selected property. * @return boolean */ protected boolean isActualProperty() { DynaClass dynaClass = dynaBean.getDynaClass(); return dynaClass.getDynaProperty(getPropertyName()) != null; } /** * Learn whether the property referenced is an indexed property. * @return boolean */ protected boolean isIndexedProperty() { DynaClass dynaClass = dynaBean.getDynaClass(); DynaProperty property = dynaClass.getDynaProperty(name); return property.isIndexed(); } /** * If index == WHOLE_COLLECTION, change the value of the property, otherwise * change the value of the index'th element of the collection * represented by the property. * @param value to set */ public void setValue(Object value) { setValue(index, value); } public void remove() { if (index == WHOLE_COLLECTION) { dynaBean.set(getPropertyName(), null); } else if (isIndexedProperty()) { dynaBean.set(getPropertyName(), index, null); } else if (isCollection()) { Object collection = ValueUtils.remove(getBaseValue(), index); dynaBean.set(getPropertyName(), collection); } else if (index == 0) { dynaBean.set(getPropertyName(), null); } } /** * Set an indexed value. * @param index to change * @param value to set */ private void setValue(int index, Object value) { if (index == WHOLE_COLLECTION) { dynaBean.set(getPropertyName(), convert(value, false)); } else if (isIndexedProperty()) { dynaBean.set(getPropertyName(), index, convert(value, true)); } else { Object baseValue = dynaBean.get(getPropertyName()); ValueUtils.setValue(baseValue, index, value); } } /** * Convert a value to the appropriate property type. * @param value to convert * @param element whether this should be a collection element. * @return conversion result */ private Object convert(Object value, boolean element) { DynaClass dynaClass = (DynaClass) dynaBean.getDynaClass(); DynaProperty property = dynaClass.getDynaProperty(getPropertyName()); Class type = property.getType(); if (element) { if (type.isArray()) { type = type.getComponentType(); } else { return value; // No need to convert } } try { return TypeUtils.convert(value, type); } catch (Exception ex) { String string = value == null ? "null" : value.getClass().getName(); throw new JXPathTypeConversionException( "Cannot convert value of class " + string + " to type " + type, ex); } } }