001    /*
002     *  Copyright 2001-2013 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.beans.impl.direct;
017    
018    import java.util.AbstractCollection;
019    import java.util.AbstractMap;
020    import java.util.AbstractSet;
021    import java.util.Arrays;
022    import java.util.Collection;
023    import java.util.Iterator;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import org.joda.beans.MetaProperty;
028    
029    /**
030     * A map of name to meta-property designed for use by {@code DirectBean}.
031     * <p>
032     * This meta-property map implementation is designed primarily for code-generation.
033     * It stores a reference to the meta-bean and the meta-properties.
034     * The meta-properties are accessed using {@link DirectMetaBean#metaPropertyGet(String)}.
035     * <p>
036     * This class is immutable and thread-safe.
037     * 
038     * @author Stephen Colebourne
039     */
040    @SuppressWarnings("rawtypes")
041    public final class DirectMetaPropertyMap implements Map<String, MetaProperty<?>> {
042    
043        /** The meta-bean. */
044        private final DirectMetaBean metaBean;
045        /** The property names. */
046        private final Set<String> keys;
047        /** The meta-properties. */
048        private final Collection<MetaProperty<?>> values;
049        /** The map entries. */
050        private final Set<Entry<String, MetaProperty<?>>> entries;
051    
052        /**
053         * Constructor.
054         * 
055         * @param metaBean  the meta-bean, not null
056         * @param parent  the superclass parent, may be null
057         * @param propertyNames  the property names, not null
058         */
059        @SuppressWarnings("unchecked")
060        public DirectMetaPropertyMap(final DirectMetaBean metaBean, DirectMetaPropertyMap parent, String... propertyNames) {
061            if (metaBean == null) {
062                throw new NullPointerException("MetaBean must not be null");
063            }
064            this.metaBean = metaBean;
065            int parentSize = 0;
066            final Entry<String, MetaProperty<?>>[] metaProperties;
067            if (parent != null) {
068                parentSize = parent.size();
069                metaProperties = Arrays.copyOf(((Entries) parent.entries).metaProperties, parentSize + propertyNames.length);
070            } else {
071                metaProperties = new Entry[propertyNames.length];
072            }
073            for (int i = 0 ; i < propertyNames.length; i++) {
074                metaProperties[i + parentSize] = new AbstractMap.SimpleImmutableEntry(propertyNames[i], metaBean.metaPropertyGet(propertyNames[i]));
075            }
076            keys = new Keys(metaProperties);
077            values = new Values(metaProperties);
078            entries = new Entries(metaProperties);
079        }
080    
081        //-----------------------------------------------------------------------
082        @Override
083        public int size() {
084            return keys.size();
085        }
086    
087        @Override
088        public boolean isEmpty() {
089            return size() == 0;
090        }
091    
092        @SuppressWarnings("unchecked")
093        @Override
094        public MetaProperty<Object> get(Object propertyName) {
095            if (propertyName  instanceof String) {
096                return (MetaProperty<Object>) metaBean.metaPropertyGet((String) propertyName);
097            }
098            return null;
099        }
100    
101        @Override
102        public boolean containsKey(Object propertyName) {
103            return propertyName instanceof String &&
104                    metaBean.metaPropertyGet(propertyName.toString()) != null;
105        }
106    
107        @Override
108        public boolean containsValue(Object value) {
109            return value instanceof MetaProperty &&
110                    metaBean.metaPropertyGet(((MetaProperty<?>) value).name()) != null;
111        }
112    
113        //-----------------------------------------------------------------------
114        @Override
115        public MetaProperty<?> put(String key, MetaProperty<?> value) {
116            throw new UnsupportedOperationException("DirectBean meta-property map cannot be modified");
117        }
118    
119        @Override
120        public MetaProperty<?> remove(Object key) {
121            throw new UnsupportedOperationException("DirectBean meta-property map cannot be modified");
122        }
123    
124        @Override
125        public void putAll(Map<? extends String, ? extends MetaProperty<?>> m) {
126            throw new UnsupportedOperationException("DirectBean meta-property map cannot be modified");
127        }
128    
129        @Override
130        public void clear() {
131            throw new UnsupportedOperationException("DirectBean meta-property map cannot be modified");
132        }
133    
134        //-----------------------------------------------------------------------
135        @Override
136        public Set<String> keySet() {
137            return keys;
138        }
139    
140        @Override
141        public Collection<MetaProperty<?>> values() {
142            return values;
143        }
144    
145        @Override
146        public Set<Entry<String, MetaProperty<?>>> entrySet() {
147            return entries;
148        }
149    
150        //-----------------------------------------------------------------------
151        /**
152         * Collection implementation for the keys.
153         */
154        private static final class Keys extends AbstractSet<String> {
155            private final Entry<String, MetaProperty<?>>[] metaProperties;
156    
157            private Keys(Entry<String, MetaProperty<?>>[] metaProperties) {
158                this.metaProperties = metaProperties;
159            }
160    
161            @Override
162            public Iterator<String> iterator() {
163                return new Iterator<String>() {
164                    int index;
165                    @Override
166                    public boolean hasNext() {
167                        return index < metaProperties.length;
168                    }
169                    @Override
170                    public String next() {
171                        return metaProperties[index++].getKey();
172                    }
173                    @Override
174                    public void remove() {
175                        throw new UnsupportedOperationException();
176                    }
177                };
178            }
179    
180            @Override
181            public int size() {
182                return metaProperties.length;
183            }
184        }
185    
186        /**
187         * Collection implementation for the values.
188         */
189        private static final class Values extends AbstractCollection<MetaProperty<?>> {
190            private final Entry<String, MetaProperty<?>>[] metaProperties;
191    
192            private Values(Entry<String, MetaProperty<?>>[] metaProperties) {
193                this.metaProperties = metaProperties;
194            }
195    
196            @Override
197            public Iterator<MetaProperty<?>> iterator() {
198                return new Iterator<MetaProperty<?>>() {
199                    int index;
200                    @Override
201                    public boolean hasNext() {
202                        return index < metaProperties.length;
203                    }
204                    @Override
205                    public MetaProperty<?> next() {
206                        return metaProperties[index++].getValue();
207                    }
208                    @Override
209                    public void remove() {
210                        throw new UnsupportedOperationException();
211                    }
212                };
213            }
214    
215            @Override
216            public int size() {
217                return metaProperties.length;
218            }
219        }
220    
221        /**
222         * Collection implementation for the entries.
223         */
224        private static final class Entries extends AbstractSet<Entry<String, MetaProperty<?>>> {
225            private final Entry<String, MetaProperty<?>>[] metaProperties;
226    
227            private Entries(Entry<String, MetaProperty<?>>[] metaProperties) {
228                this.metaProperties = metaProperties;
229            }
230    
231            @Override
232            public Iterator<Entry<String, MetaProperty<?>>> iterator() {
233                return new Iterator<Entry<String, MetaProperty<?>>>() {
234                    int index;
235                    @Override
236                    public boolean hasNext() {
237                        return index < metaProperties.length;
238                    }
239                    @Override
240                    public Entry<String, MetaProperty<?>> next() {
241                        return metaProperties[index++];
242                    }
243                    @Override
244                    public void remove() {
245                        throw new UnsupportedOperationException();
246                    }
247                };
248            }
249    
250            @Override
251            public int size() {
252                return metaProperties.length;
253            }
254        }
255    
256    }