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.beans.BeanInfo;
045: import java.beans.Introspector;
046: import java.beans.PropertyDescriptor;
047: import java.io.Serializable;
048: import java.lang.reflect.Constructor;
049: import java.lang.reflect.Field;
050: import java.lang.reflect.Modifier;
051: import java.math.BigDecimal;
052: import java.math.BigInteger;
053: import java.util.ArrayList;
054: import java.util.Arrays;
055: import java.util.Calendar;
056: import java.util.Collection;
057: import com.sun.data.provider.DataListener;
058: import com.sun.data.provider.DataProvider;
059: import com.sun.data.provider.DataProviderException;
060: import com.sun.data.provider.FieldKey;
061:
062: /**
063: * Abstract base implementation of {@link DataProvider}. This class is a
064: * convenient base class to use when creating a new {@link DataProvider}
065: * implementation.
066: *
067: * @author Joe Nuxoll
068: */
069: public abstract class AbstractDataProvider implements DataProvider,
070: Serializable {
071:
072: // -------------------------------------------------------- Abstract Methods
073:
074: /** {@inheritDoc} */
075: abstract public Class getType(FieldKey fieldKey)
076: throws DataProviderException;
077:
078: /** {@inheritDoc} */
079: abstract public boolean isReadOnly(FieldKey fieldKey)
080: throws DataProviderException;
081:
082: /** {@inheritDoc} */
083: abstract public Object getValue(FieldKey fieldKey)
084: throws DataProviderException;
085:
086: /** {@inheritDoc} */
087: abstract public void setValue(FieldKey fieldKey, Object value)
088: throws DataProviderException;
089:
090: // ----------------------------------------------------- Convenience Methods
091:
092: public Class getType(String fieldId) throws DataProviderException {
093: return getType(getFieldKey(fieldId));
094: }
095:
096: public boolean isReadOnly(String fieldId)
097: throws DataProviderException {
098: return isReadOnly(getFieldKey(fieldId));
099: }
100:
101: public Object getValue(String fieldId) throws DataProviderException {
102: return getValue(getFieldKey(fieldId));
103: }
104:
105: public void setValue(String fieldId, Object value)
106: throws DataProviderException {
107: setValue(getFieldKey(fieldId), value);
108: }
109:
110: // -------------------------------------------------------- FieldKey Methods
111:
112: /**
113: * storage for the list of {@link FieldKey}s in this {@link DataProvider}
114: */
115: protected FieldKey[] fieldKeys = FieldKey.EMPTY_ARRAY;
116:
117: /**
118: * Adds a {@link FieldKey} to the list of keys
119: * @param fieldKey FieldKey to add to the list of keys
120: */
121: protected void addFieldKey(FieldKey fieldKey) {
122: if (fieldKeys == null) {
123: fieldKeys = new FieldKey[1];
124: fieldKeys[0] = fieldKey;
125: return;
126: }
127: FieldKey[] results = new FieldKey[fieldKeys.length + 1];
128: System.arraycopy(fieldKeys, 0, results, 0, fieldKeys.length);
129: results[results.length - 1] = fieldKey;
130: fieldKeys = results;
131: fireProviderChanged();
132: }
133:
134: /**
135: * Adds a list of {@link FieldKey}s to the list of keys
136: * @param fieldKeys FieldKey[] to add to the list of keys
137: */
138: protected void addFieldKeys(FieldKey[] fieldKeys) {
139: for (int i = 0; i < fieldKeys.length; i++) {
140: addFieldKey(fieldKeys[i]);
141: }
142: fireProviderChanged();
143: }
144:
145: /**
146: * Sorts the {@link FieldKey}s (using Arrays.sort)
147: */
148: protected void sortFieldKeys() {
149: Arrays.sort(fieldKeys);
150: }
151:
152: /**
153: * Removes a {@link FieldKey} from the list of keys
154: * @param fieldKey FieldKey to remove from the list
155: */
156: protected void removeFieldKey(FieldKey fieldKey) {
157: if (fieldKeys == null) {
158: return;
159: }
160: ArrayList list = new ArrayList(fieldKeys.length - 1);
161: for (int i = 0; i < fieldKeys.length; i++) {
162: if (fieldKeys[i] != fieldKey
163: && !fieldKeys[i].equals(fieldKey)) {
164: list.add(fieldKeys[i]);
165: }
166: }
167: fieldKeys = (FieldKey[]) list
168: .toArray(new FieldKey[list.size() - 1]);
169: fireProviderChanged();
170: }
171:
172: /**
173: * Removes an array of {@link FieldKey}s from the list
174: * @param fieldKeys FieldKey[] to remove from the list
175: */
176: protected void removeFieldKeys(FieldKey[] fieldKeys) {
177: for (int i = 0; i < fieldKeys.length; i++) {
178: removeFieldKey(fieldKeys[i]);
179: }
180: fireProviderChanged();
181: }
182:
183: /**
184: * Empties the list of {@link FieldKey}s
185: */
186: protected void clearFieldKeys() {
187: fieldKeys = FieldKey.EMPTY_ARRAY;
188: fireProviderChanged();
189: }
190:
191: /** {@inheritDoc} */
192: public FieldKey[] getFieldKeys() throws DataProviderException {
193: return fieldKeys;
194: }
195:
196: /** {@inheritDoc} */
197: public FieldKey getFieldKey(String fieldId)
198: throws DataProviderException {
199: for (int i = 0; fieldKeys != null && i < fieldKeys.length; i++) {
200: FieldKey fk = fieldKeys[i];
201: if (fieldId == fk.getFieldId()
202: || fieldId.equals(fk.getFieldId())) {
203: return fk;
204: }
205: }
206: return null;
207: }
208:
209: // ------------------------------------------------------- Fake Data Methods
210:
211: /**
212: * This helper method defers to {@link #getFakeData(Class, Class)} passing
213: * null as the collectionElementType. This is used for design-time only
214: * rendering of fake data.
215: *
216: * @param dataType The Class representing the type of data to fake
217: * @return The fake data object
218: */
219: public static Object getFakeData(Class dataType) {
220: return getFakeData(dataType, null);
221: }
222:
223: /**
224: * <p>This helper method produces fake data for use during design-time.
225: * This allows a DataProvider implementation class to avoid a costly call
226: * to a database, web service, or EJB method at design-time. This will
227: * handle array and collection types as well as object types.</p>
228: *
229: * <p>If the underlying type is other than a primitive type or java.lang
230: * common type, it is constructed by attempting to use the default
231: * constructor for the class, if it exists. The method will then recurse
232: * to populate the properties and fields of the nested object (avoiding
233: * recursion). If no default constructor exists, this method will return
234: * null for the value.</p>
235: *
236: * @param dataType The Class representing the type of data to fake. This
237: * might be a native array, Collection, or any Object subtype
238: * @param collectionElementType If the 'dataType' is a Collection type, this
239: * parameter specifies the type of element stored in the Collection
240: * @return The fake data object
241: */
242: public static Object getFakeData(Class dataType,
243: Class collectionElementType) {
244: return getFakeData(dataType, collectionElementType,
245: new ArrayList());
246: }
247:
248: private static Object getFakeData(Class dataType,
249: Class collectionElementType, ArrayList recurseCheckClassList) {
250:
251: if (dataType == null || Void.TYPE == dataType) {
252: return null;
253:
254: } else if (dataType.isArray()) {
255:
256: collectionElementType = dataType.getComponentType();
257: Object[] result = new Object[3];
258: for (int i = 0; i < result.length; i++) {
259: if (!recurseCheckClassList
260: .contains(collectionElementType)) {
261: recurseCheckClassList.add(collectionElementType);
262: result[i] = getFakeData(collectionElementType,
263: null, recurseCheckClassList);
264: recurseCheckClassList.remove(collectionElementType);
265: }
266: }
267: return result;
268:
269: } else if (Collection.class.isAssignableFrom(dataType)) {
270:
271: ArrayList result = new ArrayList();
272: for (int i = 0; i < 3; i++) {
273: if (!recurseCheckClassList
274: .contains(collectionElementType)) {
275: recurseCheckClassList.add(collectionElementType);
276: result.add(getFakeData(collectionElementType, null,
277: recurseCheckClassList));
278: recurseCheckClassList.remove(collectionElementType);
279: } else {
280: result.add(null);
281: }
282: }
283: return result;
284:
285: } else if (dataType.isPrimitive()) {
286: if (Boolean.TYPE == dataType) {
287: return Boolean.TRUE;
288: } else if (Character.TYPE == dataType) {
289: return new Character('a'); // NOI18N
290: } else if (Byte.TYPE == dataType) {
291: return new Byte((byte) 123);
292: } else if (Short.TYPE == dataType) {
293: return new Short((short) 123);
294: } else if (Integer.TYPE == dataType) {
295: return new Integer(123);
296: } else if (Long.TYPE == dataType) {
297: return new Long(123);
298: } else if (Float.TYPE == dataType) {
299: return new Float(123.45);
300: } else if (Double.TYPE == dataType) {
301: return new Double(123.45);
302: }
303:
304: } else if (Boolean.class == dataType) {
305: return Boolean.TRUE;
306:
307: } else if (java.util.Date.class == dataType) {
308: return new java.util.Date();
309:
310: } else if (java.sql.Date.class == dataType) {
311: return new java.sql.Date(System.currentTimeMillis());
312:
313: } else if (Calendar.class == dataType) {
314: return new java.util.GregorianCalendar();
315:
316: } else if (BigDecimal.class == dataType) {
317: return new BigDecimal(123);
318:
319: } else if (BigInteger.class == dataType) {
320: return BigInteger.valueOf(123);
321:
322: } else if (Character.class == dataType) {
323: return new Character('a'); // NOI18N
324:
325: } else if (Byte.class == dataType) {
326: return new Byte((byte) 123);
327:
328: } else if (Short.class == dataType) {
329: return new Short((short) 123);
330:
331: } else if (Integer.class == dataType) {
332: return new Integer(123);
333:
334: } else if (Long.class == dataType) {
335: return new Long(123);
336:
337: } else if (Float.class == dataType) {
338: return new Float(123.45);
339:
340: } else if (Double.class == dataType) {
341: return new Double(123.45);
342:
343: } else if (String.class == dataType) {
344: return "abc"; // NOI18N
345:
346: } else {
347:
348: try {
349: Constructor con = dataType.getConstructor(new Class[0]);
350: if (con != null) {
351: Object o = con.newInstance(new Object[0]);
352: if (o != null) {
353: // attempt to populate the properties of the object
354: try {
355: BeanInfo bi = Introspector.getBeanInfo(o
356: .getClass(),
357: Introspector.IGNORE_ALL_BEANINFO);
358: PropertyDescriptor[] props = bi
359: .getPropertyDescriptors();
360: for (int i = 0; props != null
361: && i < props.length; i++) {
362: if (props[i].getWriteMethod() != null
363: && props[i].getReadMethod() != null) {
364: Class propType = props[i]
365: .getPropertyType();
366: if (!recurseCheckClassList
367: .contains(propType)) {
368: recurseCheckClassList
369: .add(propType);
370: Object data = getFakeData(
371: propType, null,
372: recurseCheckClassList);
373: recurseCheckClassList
374: .remove(propType);
375: try {
376: props[i]
377: .getWriteMethod()
378: .invoke(
379: o,
380: new Object[] { data });
381: } catch (Exception x) {
382: }
383: }
384: }
385: }
386: } catch (Exception x) {
387: }
388: // attempt to populate the public fields of the object
389: Field[] fields = o.getClass().getFields();
390: for (int i = 0; fields != null
391: && i < fields.length; i++) {
392: if (Modifier.isPublic(fields[i]
393: .getModifiers())) {
394: Class fieldType = fields[i].getType();
395: if (!recurseCheckClassList
396: .contains(fieldType)) {
397: recurseCheckClassList
398: .add(fieldType);
399: Object data = getFakeData(
400: fieldType, null,
401: recurseCheckClassList);
402: recurseCheckClassList
403: .remove(fieldType);
404: try {
405: fields[i].set(o, data);
406: } catch (Exception x) {
407: }
408: }
409: }
410: }
411: }
412: return o;
413: }
414: } catch (Exception x) {
415: return null;
416: }
417: }
418: return null;
419: }
420:
421: // ----------------------------------------------------------- Event Methods
422:
423: /**
424: * <p>Array of {@link DataListener} instances registered for
425: * this {@link DataProvider}.</p>
426: */
427: protected DataListener dpListeners[] = null;
428:
429: /** {@inheritDoc} */
430: public void addDataListener(DataListener listener) {
431: if (dpListeners == null) {
432: dpListeners = new DataListener[1];
433: dpListeners[0] = listener;
434: return;
435: }
436: DataListener results[] = new DataListener[dpListeners.length + 1];
437: System
438: .arraycopy(dpListeners, 0, results, 0,
439: dpListeners.length);
440: results[results.length - 1] = listener;
441: dpListeners = results;
442: }
443:
444: /** {@inheritDoc} */
445: public void removeDataListener(DataListener listener) {
446: if (dpListeners == null) {
447: return;
448: }
449: ArrayList list = new ArrayList(dpListeners.length - 1);
450: for (int i = 0; i < dpListeners.length; i++) {
451: if (dpListeners[i] != listener) {
452: list.add(dpListeners[i]);
453: }
454: }
455: if (list.size() < 1) {
456: dpListeners = null;
457: } else {
458: dpListeners = (DataListener[]) list
459: .toArray(new DataListener[list.size()]);
460: }
461: }
462:
463: /** {@inheritDoc} */
464: public DataListener[] getDataListeners() {
465: if (dpListeners == null) {
466: return new DataListener[0];
467: } else {
468: return dpListeners;
469: }
470: }
471:
472: /**
473: * Fires a valueChanged event to each registered {@link DataListener}
474: *
475: * @param fieldKey FieldKey identifying the changed value
476: * @param oldValue The old value (before the change)
477: * @param newValue The new value (after the change)
478: * @see DataListener#valueChanged(DataProvider, FieldKey, Object, Object)
479: */
480: protected void fireValueChanged(FieldKey fieldKey, Object oldValue,
481: Object newValue) {
482: DataListener[] dpls = getDataListeners();
483: for (int i = 0; i < dpls.length; i++) {
484: dpls[i].valueChanged(this , fieldKey, oldValue, newValue);
485: }
486: }
487:
488: /**
489: * Fires a providerChanged event to each registered {@link DataListener}
490: *
491: * @see DataListener#providerChanged(DataProvider)
492: */
493: protected void fireProviderChanged() {
494: DataListener[] dpls = getDataListeners();
495: for (int i = 0; i < dpls.length; i++) {
496: dpls[i].providerChanged(this);
497: }
498: }
499: }
|