001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.utils;
022
023import java.beans.BeanInfo;
024import java.beans.IntrospectionException;
025import java.beans.Introspector;
026import java.beans.PropertyDescriptor;
027import java.lang.reflect.InvocationTargetException;
028import java.util.AbstractMap;
029import java.util.AbstractSet;
030import java.util.HashMap;
031import java.util.Iterator;
032import java.util.Map;
033import java.util.Set;
034
035/**
036 * @author Matthew Pocock
037 */
038public class BeanAsMap
039  extends
040    AbstractMap
041{
042  // fixme: does this class work at all?
043  
044  private static Map beanInfoCache;
045  private static Object[] NO_PARAMS;
046  
047  static {
048    beanInfoCache = new HashMap();
049    NO_PARAMS = new Object[] {};
050  }
051  
052  private static BeanInfo getBeanInfo(Class clazz)
053  throws IntrospectionException {
054    BeanInfo bi = (BeanInfo) beanInfoCache.get(clazz);
055    
056    if(bi == null) {
057      beanInfoCache.put(clazz, bi = Introspector.getBeanInfo(clazz));
058    }
059    
060    return bi;
061  }
062  
063  private final BeanInfo beanInfo;
064  private final Object bean;
065  private final PropertyDescriptor[] descriptors;
066  private final Set entrySet;
067  
068  public BeanAsMap(Object bean)
069  throws IntrospectionException {
070    this.beanInfo = getBeanInfo(bean.getClass());
071    this.bean = bean;
072    this.descriptors = this.beanInfo.getPropertyDescriptors();
073    this.entrySet = new PropertySet();
074  }
075  
076  public int size() {
077    return entrySet.size();
078  }
079  
080  public Set entrySet() {
081    return entrySet;
082  }
083  
084  public Object put(Object key, Object value) {
085    for(Iterator i = entrySet.iterator(); i.hasNext(); ) {
086      PropertyEntry pe = (PropertyEntry) i.next();
087      if(pe.getKey().equals(key)) {
088        return pe.setValue(value);
089      }
090    }
091    throw new IllegalArgumentException("BeanAsMap does not support key: " + key);
092  }
093  
094  private class PropertySet
095    extends
096      AbstractSet
097  {
098    private PropertyEntry[] entries;
099    
100    public PropertySet() {
101      entries = new PropertyEntry[descriptors.length];
102      for(int i = 0; i < entries.length; i++) {
103        entries[i] = new PropertyEntry(descriptors[i]);
104      }
105    }
106    
107    public int size() {
108      return entries.length;
109    }
110    
111    public Iterator iterator() {
112      return new Iterator() {
113        int i = 0;
114        
115        public boolean hasNext() {
116          return i < entries.length;
117        }
118        
119        public Object next() {
120          return entries[i++];
121        }
122        
123        public void remove() {
124          throw new UnsupportedOperationException();
125        }
126      };
127    }
128  }
129  
130  private class PropertyEntry implements Map.Entry {
131    private final PropertyDescriptor pd;
132    
133    public PropertyEntry(PropertyDescriptor pd) {
134      this.pd = pd;
135    }
136    
137    public Object getKey() {
138      return pd.getName();
139    }
140    
141    public Object getValue() {
142      try {
143        return pd.getReadMethod().invoke(bean, NO_PARAMS);
144      } catch (IllegalAccessException iae) {
145        throw new AssertionFailure("Could not set property", iae);
146      } catch (InvocationTargetException ite) {
147        throw new AssertionFailure("Could not invoke property", ite);
148      }
149    }
150    
151    public Object setValue(Object value) {
152      try {
153        Object old = getValue();
154        pd.getWriteMethod().invoke(bean, new Object[] { value });
155        
156        return old;
157      } catch (IllegalAccessException iae) {
158        throw new AssertionFailure("Could not access property", iae);
159      } catch (InvocationTargetException ite) {
160        throw new AssertionFailure("Could not invoke property", ite);
161      }
162    }
163  }
164}