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.io.IOException;
045: import java.io.ObjectInputStream;
046: import java.io.ObjectOutputStream;
047: import java.io.Serializable;
048: import java.lang.reflect.Method;
049: import java.util.ArrayList;
050: import java.util.Collection;
051: import java.util.HashMap;
052: import com.sun.data.provider.DataListener;
053: import com.sun.data.provider.FieldKey;
054: import com.sun.data.provider.RefreshableDataListener;
055: import com.sun.data.provider.RefreshableDataProvider;
056: import com.sun.data.provider.RowKey;
057: import com.sun.data.provider.TableCursorListener;
058: import com.sun.data.provider.TableCursorVetoException;
059: import com.sun.data.provider.TableDataListener;
060: import com.sun.data.provider.TableDataProvider;
061: import com.sun.data.provider.DataProviderException;
062:
063: /**
064: * <p>A TableDataProvider implementation to wrap the return value from a method.
065: * Set the <code>dataClassInstance</code>, <code>dataMethod</code>, and
066: * <code>dataMethodArguments</code> properties to point to a method on a class
067: * instance. The result from that method call will be wrapped as a
068: * TableDataProvider, with a row for each element in the returned array or
069: * collection. If there is only a single return value, it will be a single row
070: * table.</p>
071: *
072: * @author cao, Joe Nuxoll
073: */
074: public class MethodResultTableDataProvider implements
075: TableDataProvider, RefreshableDataProvider, Serializable {
076:
077: /**
078: * Constructs a new MethodResultTableDataProvider with no dataClassInstance
079: * or dataMethod specified.
080: */
081: public MethodResultTableDataProvider() {
082: }
083:
084: /**
085: * Constructs a new MethodResultTableDataProvider using the specified
086: * dataClassInstance and dataMethod.
087: *
088: * @param dataClassInstance The class instance where the method is invoked
089: * @param dataMethod The method where the data is from
090: */
091: public MethodResultTableDataProvider(Object dataClassInstance,
092: Method dataMethod) {
093: this ();
094: setDataClassInstance(dataClassInstance);
095: setDataMethod(dataMethod);
096: }
097:
098: /**
099: * Returns the dataClassInstance that contains the dataMethod to be invoked.
100: *
101: * @return Object
102: */
103: public Object getDataClassInstance() {
104: return dataClassInstance;
105: }
106:
107: /**
108: * Sets the dataClassInstance that contains the dataMethod to be invoked.
109: *
110: * @param instance Object
111: */
112: public void setDataClassInstance(Object instance) {
113: this .dataClassInstance = instance;
114: resultProvider.clearObjectList();
115: }
116:
117: /**
118: * Returns the currently set dataMethod
119: *
120: * @return Method
121: */
122: public Method getDataMethod() {
123: return dataMethod;
124: }
125:
126: /**
127: * Sets the dataMethod that will be invoked
128: *
129: * @param method Method
130: */
131: public void setDataMethod(Method method) {
132: this .dataMethod = method;
133: resultProvider.clearObjectList();
134: refreshFieldKeys();
135: }
136:
137: /**
138: * If the dataMethod returns a <code>Collection</code> type, this property
139: * will be used to determine the appropriate FieldKeys for the elements in
140: * the collection type.
141: *
142: * @param elementType Class
143: */
144: public void setCollectionElementType(Class elementType) {
145: this .collectionElementType = elementType;
146: refreshFieldKeys();
147: }
148:
149: public Class getCollectionElementType() {
150: return collectionElementType;
151: }
152:
153: /**
154: * Read-only access to the result object from the invocation of the
155: * dataMethod
156: *
157: * @return Object
158: */
159: public Object getResultObject() throws DataProviderException {
160: testInvokeDataMethod();
161: return resultObject;
162: }
163:
164: /**
165: * Read-only access to the result object array from the invocation of the
166: * dataMethod
167: *
168: * @return Object[]
169: */
170: public Object[] getResultObjects() throws DataProviderException {
171: testInvokeDataMethod();
172: if (resultObject instanceof Object[]) {
173: return (Object[]) resultObject;
174: } else if (resultObject instanceof Collection) {
175: return ((Collection) resultObject).toArray();
176: } else if (resultObject != null) {
177: return new Object[] { resultObject };
178: }
179: return null;
180: }
181:
182: /**
183: * <p>Sets the includeFields property. This affects the set of {@link
184: * FieldKey}s that this {@link com.sun.data.provider.DataProvider} emits.
185: * If includeFields is set to true (the default), then public fields will
186: * be included in the list of available keys (intermixed with the public
187: * properties). If it is set to false, then only the public properties
188: * will be available.</p>
189: *
190: * @param includeFields <code>true</code> to include the public fields, or
191: * <code>false</code> to exclude them (and only show public
192: * properties)
193: */
194: public void setIncludeFields(boolean includeFields) {
195: resultProvider.setIncludeFields(includeFields);
196: }
197:
198: /**
199: * @return The boolean state of the includeFields property
200: */
201: public boolean isIncludeFields() {
202: return resultProvider.isIncludeFields();
203: }
204:
205: /**
206: * Refreshes the list of available fieldKeys (based on the return type of
207: * the dataMethod)
208: */
209: protected void refreshFieldKeys() {
210: resultProvider.clearFieldKeys();
211: if (dataMethod != null) {
212: Class returnType = dataMethod.getReturnType();
213: if (returnType.isArray()) {
214: returnType = returnType.getComponentType();
215: } else if (Collection.class.isAssignableFrom(returnType)) {
216: returnType = collectionElementType;
217: }
218: resultProvider.setObjectType(returnType);
219: }
220: }
221:
222: /**
223: * Sets the dataMethodArguments, which will be passed to the dataMethod
224: * when it is invoked.
225: *
226: * @param methodArgs Object[]
227: */
228: public void setDataMethodArguments(Object[] methodArgs) {
229: this .dataMethodArgs = methodArgs;
230: resultObject = null;
231: resultProvider.clearObjectList();
232: }
233:
234: /**
235: * Returns the dataMethodArguments
236: *
237: * @return Object[]
238: */
239: public Object[] getDataMethodArguments() {
240: return dataMethodArgs;
241: }
242:
243: //-------------------------------------------------------- Method Invocation
244:
245: /**
246: * Invokes the dataMethod using the arguments specified by the
247: * dataMethodArguments property.
248: */
249: public void invokeDataMethod() throws DataProviderException {
250: invokeDataMethod(getDataMethodArguments());
251: }
252:
253: /**
254: * Invokes the dataMethod using the specified arguments.
255: *
256: * @param args Object[]
257: */
258: public void invokeDataMethod(Object[] args)
259: throws DataProviderException {
260: this .dataMethodArgs = args;
261: this .resultObject = null;
262: resultProvider.clearObjectList();
263: if (dataMethod == null || dataClassInstance == null) {
264: return;
265: }
266: try {
267: if (java.beans.Beans.isDesignTime()) {
268: // fake a call to the underlying data method for design-time
269: resultObject = AbstractDataProvider.getFakeData(
270: dataMethod.getReturnType(),
271: collectionElementType);
272: } else {
273: resultObject = dataMethod.invoke(dataClassInstance,
274: args);
275: }
276: if (resultObject instanceof Object[]) {
277: Object[] oa = (Object[]) resultObject;
278: for (int i = 0; i < oa.length; i++) {
279: resultProvider.addObject(oa[i]);
280: }
281: } else if (resultObject instanceof Collection) {
282: Object[] oa = ((Collection) resultObject).toArray();
283: for (int i = 0; i < oa.length; i++) {
284: resultProvider.addObject(oa[i]);
285: }
286: } else if (resultObject != null) {
287: resultProvider.addObject(resultObject);
288: }
289: //!FIXME - this is a temporary hack to workaround an issue with
290: // ObjectListDataProvider - this should be removed when OLDP is fixed
291: resultProvider.commitChanges();
292: fireRefreshed();
293: } catch (Exception e) {
294: throw new DataProviderException(e);
295: }
296: }
297:
298: /**
299: * Tests to see if the dataMethod has been invoked, and invokes it if it
300: * has not.
301: */
302: protected void testInvokeDataMethod() throws DataProviderException {
303: if (resultObject == null || resultProvider.getRowCount() == 0) {
304: invokeDataMethod();
305: }
306: }
307:
308: // ---------------------------------- RefreshableDataProvider Implementation
309:
310: /**
311: * Invokes the dataMethod on the dataClassInstance to refresh the data
312: * provider's contets
313: */
314: public void refresh() throws DataProviderException {
315: invokeDataMethod();
316: }
317:
318: /** {@inheritDoc} */
319: public void addRefreshableDataListener(RefreshableDataListener l) {
320: resultProvider.addDataListener(l);
321: }
322:
323: /** {@inheritDoc} */
324: public void removeRefreshableDataListener(RefreshableDataListener l) {
325: resultProvider.removeDataListener(l);
326: }
327:
328: /** {@inheritDoc} */
329: public RefreshableDataListener[] getRefreshableDataListeners() {
330: DataListener[] dpListeners = resultProvider.getDataListeners();
331: if (dpListeners == null) {
332: return new RefreshableDataListener[0];
333: } else {
334: ArrayList rdlList = new ArrayList();
335: for (int i = 0; i < dpListeners.length; i++) {
336: if (dpListeners[i] instanceof RefreshableDataListener) {
337: rdlList.add(dpListeners[i]);
338: }
339: }
340: return (RefreshableDataListener[]) rdlList
341: .toArray(new RefreshableDataListener[rdlList.size()]);
342: }
343: }
344:
345: /**
346: * Fires a refreshed event to each registered {@link RefreshableDataListener}
347: *
348: * @see RefreshableDataListener#refreshed(RefreshableDataProvider)
349: */
350: protected void fireRefreshed() {
351: RefreshableDataListener[] rdls = getRefreshableDataListeners();
352: for (int i = 0; i < rdls.length; i++) {
353: rdls[i].refreshed(this );
354: }
355: }
356:
357: private Object dataClassInstance;
358: private transient Method dataMethod;
359: private Class collectionElementType;
360: private Object[] dataMethodArgs;
361: private Object resultObject;
362: private ObjectListDataProvider resultProvider = new ObjectListDataProvider();
363:
364: private void writeObject(ObjectOutputStream out) throws IOException {
365: if (dataMethod != null) {
366: HashMap sig = new HashMap();
367: sig.put("class", dataMethod.getDeclaringClass()); // NOI18N
368: sig.put("name", dataMethod.getName()); // NOI18N
369: sig.put("params", dataMethod.getParameterTypes()); // NOI18N
370: out.writeObject(sig);
371: }
372: }
373:
374: private void readObject(ObjectInputStream in) throws IOException,
375: ClassNotFoundException {
376: Object o = in.readObject();
377: if (o instanceof HashMap) {
378: HashMap sig = (HashMap) o;
379: Class clazz = (Class) sig.get("class"); // NOI18N
380: String name = (String) sig.get("name"); // NOI18N
381: Class[] params = (Class[]) sig.get("params"); // NOI18N
382: try {
383: this .dataMethod = clazz.getMethod(name, params);
384: } catch (NoSuchMethodException nsmx) {
385: }
386: }
387: }
388:
389: //---------------------------------------------- DataProvider Implementation
390:
391: /** {@inheritDoc} */
392: public FieldKey[] getFieldKeys() throws DataProviderException {
393: return resultProvider.getFieldKeys();
394: }
395:
396: /** {@inheritDoc} */
397: public FieldKey getFieldKey(String fieldId)
398: throws DataProviderException {
399: return resultProvider.getFieldKey(fieldId);
400: }
401:
402: /** {@inheritDoc} */
403: public Class getType(FieldKey fieldKey)
404: throws DataProviderException {
405: return resultProvider.getType(fieldKey);
406: }
407:
408: /** {@inheritDoc} */
409: public Object getValue(FieldKey fieldKey)
410: throws DataProviderException {
411: testInvokeDataMethod();
412: return resultProvider.getValue(fieldKey);
413: }
414:
415: /** {@inheritDoc} */
416: public boolean isReadOnly(FieldKey fieldKey)
417: throws DataProviderException {
418: testInvokeDataMethod();
419: return resultProvider.isReadOnly(fieldKey);
420: }
421:
422: /** {@inheritDoc} */
423: public void setValue(FieldKey fieldKey, Object value)
424: throws DataProviderException {
425: testInvokeDataMethod();
426: resultProvider.setValue(fieldKey, value);
427: }
428:
429: /** {@inheritDoc} */
430: public void addDataListener(DataListener listener) {
431: resultProvider.addDataListener(listener);
432: }
433:
434: /** {@inheritDoc} */
435: public void removeDataListener(DataListener listener) {
436: resultProvider.removeDataListener(listener);
437: }
438:
439: /** {@inheritDoc} */
440: public DataListener[] getDataListeners() {
441: return resultProvider.getDataListeners();
442: }
443:
444: //----------------------------------------- TableDataProvider Implementation
445:
446: /** {@inheritDoc} */
447: public int getRowCount() throws DataProviderException {
448: testInvokeDataMethod();
449: return resultProvider.getRowCount();
450: }
451:
452: /** {@inheritDoc} */
453: public boolean isRowAvailable(RowKey row)
454: throws DataProviderException {
455: testInvokeDataMethod();
456: return resultProvider.isRowAvailable(row);
457: }
458:
459: /** {@inheritDoc} */
460: public RowKey[] getRowKeys(int count, RowKey afterRow)
461: throws DataProviderException {
462: testInvokeDataMethod();
463: return resultProvider.getRowKeys(count, afterRow);
464: }
465:
466: /** {@inheritDoc} */
467: public RowKey getRowKey(String rowId) throws DataProviderException {
468: testInvokeDataMethod();
469: return resultProvider.getRowKey(rowId);
470: }
471:
472: /** {@inheritDoc} */
473: public Object getValue(FieldKey fieldKey, RowKey row)
474: throws DataProviderException {
475: testInvokeDataMethod();
476: return resultProvider.getValue(fieldKey, row);
477: }
478:
479: /** {@inheritDoc} */
480: public void setValue(FieldKey fieldKey, RowKey row, Object value)
481: throws DataProviderException {
482: testInvokeDataMethod();
483: resultProvider.setValue(fieldKey, row, value);
484: }
485:
486: /** {@inheritDoc} */
487: public boolean canInsertRow(RowKey beforeRow)
488: throws DataProviderException {
489: testInvokeDataMethod();
490: return resultProvider.canInsertRow(beforeRow);
491: }
492:
493: /** {@inheritDoc} */
494: public RowKey insertRow(RowKey beforeRow)
495: throws DataProviderException {
496: testInvokeDataMethod();
497: return resultProvider.insertRow(beforeRow);
498: }
499:
500: /** {@inheritDoc} */
501: public boolean canAppendRow() throws DataProviderException {
502: testInvokeDataMethod();
503: return resultProvider.canAppendRow();
504: }
505:
506: /** {@inheritDoc} */
507: public RowKey appendRow() throws DataProviderException {
508: testInvokeDataMethod();
509: return resultProvider.appendRow();
510: }
511:
512: /** {@inheritDoc} */
513: public boolean canRemoveRow(RowKey row)
514: throws DataProviderException {
515: testInvokeDataMethod();
516: return resultProvider.canRemoveRow(row);
517: }
518:
519: /** {@inheritDoc} */
520: public void removeRow(RowKey row) throws DataProviderException {
521: testInvokeDataMethod();
522: resultProvider.removeRow(row);
523: }
524:
525: /** {@inheritDoc} */
526: public void addTableDataListener(TableDataListener l) {
527: resultProvider.addTableDataListener(l);
528: }
529:
530: /** {@inheritDoc} */
531: public void removeTableDataListener(TableDataListener l) {
532: resultProvider.removeTableDataListener(l);
533: }
534:
535: /** {@inheritDoc} */
536: public TableDataListener[] getTableDataListeners() {
537: return resultProvider.getTableDataListeners();
538: }
539:
540: /** {@inheritDoc} */
541: public RowKey getCursorRow() throws DataProviderException {
542: testInvokeDataMethod();
543: return resultProvider.getCursorRow();
544: }
545:
546: /** {@inheritDoc} */
547: public void setCursorRow(RowKey row)
548: throws TableCursorVetoException {
549: testInvokeDataMethod();
550: resultProvider.setCursorRow(row);
551: }
552:
553: /** {@inheritDoc} */
554: public boolean cursorFirst() throws DataProviderException {
555: testInvokeDataMethod();
556: return resultProvider.cursorFirst();
557: }
558:
559: /** {@inheritDoc} */
560: public boolean cursorPrevious() throws DataProviderException {
561: testInvokeDataMethod();
562: return resultProvider.cursorPrevious();
563: }
564:
565: /** {@inheritDoc} */
566: public boolean cursorNext() throws DataProviderException {
567: testInvokeDataMethod();
568: return resultProvider.cursorNext();
569: }
570:
571: /** {@inheritDoc} */
572: public boolean cursorLast() throws DataProviderException {
573: testInvokeDataMethod();
574: return resultProvider.cursorLast();
575: }
576:
577: /** {@inheritDoc} */
578: public void addTableCursorListener(TableCursorListener l) {
579: resultProvider.addTableCursorListener(l);
580: }
581:
582: /** {@inheritDoc} */
583: public void removeTableCursorListener(TableCursorListener l) {
584: resultProvider.removeTableCursorListener(l);
585: }
586:
587: /** {@inheritDoc} */
588: public TableCursorListener[] getTableCursorListeners() {
589: return resultProvider.getTableCursorListeners();
590: }
591: }
|