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 */
016package org.joda.beans.impl.direct;
017
018import java.util.AbstractCollection;
019import java.util.AbstractMap;
020import java.util.AbstractSet;
021import java.util.Arrays;
022import java.util.Collection;
023import java.util.Iterator;
024import java.util.Map;
025import java.util.Set;
026
027import 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")
041public 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                private 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                private 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                private 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}