001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2011-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import java.lang.reflect.Field;
013import java.lang.reflect.Modifier;
014import java.util.*;
015
016/**
017 * View of an object as a map. Each attribute name is an key, and the value of
018 * the attribute is the value.
019 *
020 * <p>Currently only returns public fields, not methods.</p>
021 */
022public class BeanMap extends AbstractMap<String, Object> {
023    private final Object o;
024    private final Info info;
025    private static final Map<Class, Info> INFO_MAP =
026        new WeakHashMap<Class, Info>();
027
028    /**
029     * Creates a map view onto an object.
030     *
031     * @param o Object
032     */
033    public BeanMap(Object o) {
034        this.o = o;
035        this.info = getInfo(o.getClass());
036    }
037
038    private static Info getInfo(Class<?> clazz) {
039        synchronized (INFO_MAP) {
040            Info info = INFO_MAP.get(clazz);
041            if (info == null) {
042                info = new Info(clazz);
043                INFO_MAP.put(clazz, info);
044            }
045            return info;
046        }
047    }
048
049    @Override
050    public Set<Entry<String, Object>> entrySet() {
051        return new AbstractSet<Entry<String, Object>>() {
052            @Override
053            public Iterator<Entry<String, Object>> iterator() {
054                final Iterator<BeanField> fieldIterator =
055                    info.fields.iterator();
056                return new Iterator<Entry<String, Object>>() {
057                    public boolean hasNext() {
058                        return fieldIterator.hasNext();
059                    }
060
061                    public Entry<String, Object> next() {
062                        final BeanField field = fieldIterator.next();
063                        return Pair.of(field.name(), field.value(o));
064                    }
065
066                    public void remove() {
067                        throw new UnsupportedOperationException();
068                    }
069                };
070            }
071
072            @Override
073            public int size() {
074                return info.fields.size();
075            }
076        };
077    }
078
079    public static String xxx(Object o) {
080        return o.getClass().getSimpleName() + new BeanMap(o);
081    }
082
083    private static class Info {
084        private List<BeanField> fields = new ArrayList<BeanField>();
085
086        public Info(Class<? extends Object> clazz) {
087            for (final Field field : clazz.getDeclaredFields()) {
088                final int mod = field.getModifiers();
089                if (Modifier.isStatic(mod)
090                    || !Modifier.isPublic(mod))
091                {
092                    continue;
093                }
094                fields.add(
095                    new BeanField() {
096                        public String name() {
097                            return field.getName();
098                        }
099
100                        public Object value(Object o) {
101                            try {
102                                return field.get(o);
103                            } catch (IllegalAccessException e) {
104                                return null;
105                            }
106                        }
107                    }
108                );
109            }
110        }
111    }
112
113    private interface BeanField {
114        String name();
115        Object value(Object o);
116    }
117}
118
119// End BeanMap.java