/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.sys.util;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* A Map implementation which wraps a Java bean and can return values from that based on property Strings. Unlike Apache Common's BeanMap, this Map can handle nested properties - though for the sake of that power,
* the Map makes no effort to know its Set of keys at all - since to figure all of that out, it would need to recurse down and avoid property cycles, etc. It does not know how many Entries it has. Furthermore,
* one cannot put any values into the Map and expect the underlying bean to have their own values changed. It's really a read-only way to read nested properties from the underlying object via Map semantics.
*/
public class ReflectionMap implements Map<String, Object> {
protected Object bean;
protected PropertyUtilsBean propertyUtilsBean;
public ReflectionMap(Object bean) {
if (ObjectUtils.isNull(bean)) {
throw new IllegalArgumentException("This cannot wrap a null object");
}
this.bean = bean;
this.propertyUtilsBean = new PropertyUtilsBean(); // create our own beanUtilsBean to avoid struts injection of KNS classes
}
@Override
public int size() {
throw new UnsupportedOperationException("Cannot determine possible size of recursive structure");
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException("Will not inspect every value within recursive structure");
}
@Override
public boolean containsKey(Object key) {
final Object value = get(key);
return !ObjectUtils.isNull(value);
}
@Override
public Object get(Object key) {
final String keyAsString = (String)key;
Object value;
try {
value = propertyUtilsBean.getProperty(this.bean, keyAsString);
}
catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | IndexOutOfBoundsException ex) {
// yep - we know we're swallowing the exception here. However, we know that bean can't be null, so
// to fit within regular map semantics, we're just going to return null for the missing key
return null;
}
return value;
}
@Override
public Object put(String key, Object value) {
throw new UnsupportedOperationException("ReflectionMap is read-only");
}
@Override
public Object remove(Object key) {
throw new UnsupportedOperationException("ReflectionMap is read-only");
}
@Override
public void putAll(Map m) {
throw new UnsupportedOperationException("ReflectionMap is read-only");
}
@Override
public void clear() {
throw new UnsupportedOperationException("ReflectionMap is read-only");
}
@Override
public Set<String> keySet() {
throw new UnsupportedOperationException("Cannot determine all keys within recursive structure");
}
@Override
public Collection<Object> values() {
throw new UnsupportedOperationException("Cannot determine all values within recursive structure");
}
@Override
public Set<java.util.Map.Entry<String, Object>> entrySet() {
throw new UnsupportedOperationException("Cannot determine all entries within recursive structure");
}
}