001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package com.sun.data.provider.impl;
043:
044: import java.util.ArrayList;
045: import java.util.HashMap;
046: import java.util.Iterator;
047: import java.util.Map;
048: import com.sun.data.provider.DataProviderException;
049: import com.sun.data.provider.FieldKey;
050:
051: /**
052: * <p>This {@link com.sun.data.provider.DataProvider} wraps access to a standard
053: * {@link Map}. This class can use regular {@link FieldKey} objects as keys
054: * (Map key will be the FieldKey's fieldId), or can use {@link MapFieldKey}
055: * objects if a non-string key is desired.</p>
056: *
057: * <p>NOTE about Serializable: By default, this class uses a {@link HashMap}
058: * as its internal data storage, which is a Serializable implementation of
059: * {@link Map}. The internal storage can be swapped out using the
060: * <code>setMap(Map)</code> method. For this class to remain Serializable,
061: * the contained Map must be a Serializable implementation. Also, and more
062: * importantly, the contents of the storage Map must be Serializable as well
063: * for this class to successfully be serialized.</p>
064: *
065: * @author Joe Nuxoll
066: */
067: public class MapDataProvider extends AbstractDataProvider {
068:
069: /**
070: * MapFieldKey is a {@link FieldKey} that contains an untyped Object to use
071: * as a key for a map value.
072: */
073: public class MapFieldKey extends FieldKey {
074:
075: /**
076: * Constructs a new MapFieldKey using the specified mapKey as the object
077: * map key.
078: *
079: * @param mapKey The desired Object to use as a map key
080: */
081: public MapFieldKey(Object mapKey) {
082: super (String.valueOf(mapKey));
083: this .mapKey = mapKey;
084: }
085:
086: /**
087: * Storage for the object map key
088: */
089: protected Object mapKey;
090:
091: /**
092: * @return The Object map key
093: */
094: public Object getMapKey() {
095: return mapKey;
096: }
097:
098: /**
099: * Standard equals implementation. This method compares the mapKey
100: * objects if they exist - then defaults to comparing ids.
101: *
102: * @param o Object to compare equality
103: * @return true if equal, false if not
104: * @see FieldKey#equals(Object)
105: * @see Object#equals(Object)
106: */
107: public boolean equals(Object o) {
108: if (o instanceof MapFieldKey) {
109: MapFieldKey mdk = (MapFieldKey) o;
110: return mapKey == mdk.mapKey
111: || (mapKey != null && mapKey.equals(mdk.mapKey));
112: }
113: return super .equals(o);
114: }
115: }
116:
117: /**
118: * The internal storage {@link Map}, initially a {@link HashMap}
119: */
120: protected Map map = new HashMap();
121:
122: /**
123: * Constructs a new MapDataProvider using a default {@link HashMap} as the
124: * internal storage.
125: */
126: public MapDataProvider() {
127: }
128:
129: /**
130: * <p>Constructs a new MapDataProvider using the specified Map as the
131: * internal storage.</p>
132: *
133: * <p>NOTE about Serializable: By default, this class uses a {@link HashMap}
134: * as its internal data storage, which is a Serializable implementation of
135: * {@link Map}. The internal storage can be swapped out using the
136: * <code>setMap(Map)</code> method. For this class to remain Serializable,
137: * the contained Map must be a Serializable implementation. Also, and more
138: * importantly, the contents of the storage Map must be Serializable as well
139: * for this class to successfully be serialized.</p>
140: *
141: * @param map The Map to use as internal storage
142: */
143: public MapDataProvider(Map map) {
144: setMap(map);
145: }
146:
147: /**
148: * @return Map being used as internal storage.
149: */
150: public Map getMap() {
151: return map;
152: }
153:
154: /**
155: * <p>Sets the {@link Map} to use as internal storage.</p>
156: *
157: * <p>NOTE about Serializable: By default, this class uses a {@link HashMap}
158: * as its internal data storage, which is a Serializable implementation of
159: * {@link Map}. The internal storage can be swapped out using the
160: * <code>setMap(Map)</code> method. For this class to remain Serializable,
161: * the contained Map must be a Serializable implementation. Also, and more
162: * importantly, the contents of the storage Map must be Serializable as well
163: * for this class to successfully be serialized.</p>
164: *
165: * @param map Map to use as internal storage
166: */
167: public void setMap(Map map) {
168: this .map = map;
169: }
170:
171: /**
172: * Refreshes the list of FieldKeys to reflect the current contents of the
173: * Map. This is necessary because the storage Map could be manipulated at
174: * any time by external means.
175: */
176: protected void refreshFieldKeys() {
177: super .clearFieldKeys();
178: ArrayList keys = new ArrayList();
179: Iterator kit = map.keySet().iterator();
180: while (kit.hasNext()) {
181: Object o = kit.next();
182: if (o instanceof FieldKey) {
183: keys.add(o);
184: } else {
185: keys.add(new MapFieldKey(o));
186: }
187: }
188: super .addFieldKeys((FieldKey[]) keys.toArray(new FieldKey[keys
189: .size()]));
190: super .sortFieldKeys();
191: }
192:
193: /**
194: * @return FieldKey[] The current set of FieldKeys in the Map (after
195: * completing a refresh)
196: */
197: public FieldKey[] getFieldKeys() throws DataProviderException {
198: refreshFieldKeys();
199: return super .getFieldKeys();
200: }
201:
202: /**
203: * Returns a {@link FieldKey} corresponding to the specified id.
204: *
205: * @param fieldId The desired id to retrieve a FieldKey for
206: * @return FieldKey The FieldKey for the specified id (after completing a
207: * refresh)
208: */
209: public FieldKey getFieldKey(String fieldId)
210: throws DataProviderException {
211: refreshFieldKeys();
212: return super .getFieldKey(fieldId);
213: }
214:
215: /**
216: * Returns the value stored under the specified FieldKey in the Map. The
217: * passed FieldKey may be a {@link FieldKey} or a {@link MapFieldKey}.
218: *
219: * @param fieldKey The desired FieldKey to retieve the value for
220: * @return Object The object stored in the Map under the specified FieldKey
221: */
222: public Object getValue(FieldKey fieldKey)
223: throws DataProviderException {
224:
225: if (java.beans.Beans.isDesignTime()
226: && (map == null || map.isEmpty())) {
227: // Fill the object with design time fake data
228: map = (Map) AbstractDataProvider
229: .getFakeData(map.getClass());
230: }
231:
232: if (fieldKey instanceof MapFieldKey) {
233: return map.get(((MapFieldKey) fieldKey).mapKey);
234: }
235: return map.get(fieldKey.getFieldId());
236: }
237:
238: /**
239: *
240: * @param fieldKey FieldKey
241: * @return Class
242: */
243: public Class getType(FieldKey fieldKey)
244: throws DataProviderException {
245: Object o = getValue(fieldKey);
246: return o != null ? o.getClass() : null;
247: }
248:
249: /**
250: * None of the Map entries are read-only, so this method always returns
251: * false.
252: *
253: * @param fieldKey The specified FieldKey (ignored)
254: * @return This method will always return false, as none of the Map entries
255: * are read-only.
256: */
257: public boolean isReadOnly(FieldKey fieldKey)
258: throws DataProviderException {
259: return false;
260: }
261:
262: /**
263: * Puts the specified value in the Map under the specified FieldKey. If the
264: * passed FieldKey is a {@link MapFieldKey}, the MapFieldKey.mapKey will be
265: * used as the key, otherwise the FieldKey.id will be used as the key in the
266: * underlying Map. This method will result in a valueChanged event being
267: * fired to all {@link com.sun.data.provider.DataListener} that are
268: * listening to this DataProvider.
269: *
270: * @param fieldKey The desired FieldKey to store the value under
271: * @param value The desired Object to store in the Map
272: */
273: public void setValue(FieldKey fieldKey, Object value)
274: throws DataProviderException {
275:
276: Object oldValue = getValue(fieldKey);
277: if (fieldKey instanceof MapFieldKey) {
278: map.put(((MapFieldKey) fieldKey).mapKey, value);
279: } else {
280: map.put(fieldKey.getFieldId(), value);
281: }
282: fireValueChanged(fieldKey, oldValue, value);
283: }
284: }
|