001: package org.netbeans.modules.visualweb.dataprovider;
002:
003: /*
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
005: *
006: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
007: *
008: * The contents of this file are subject to the terms of either the GNU
009: * General Public License Version 2 only ("GPL") or the Common
010: * Development and Distribution License("CDDL") (collectively, the
011: * "License"). You may not use this file except in compliance with the
012: * License. You can obtain a copy of the License at
013: * http://www.netbeans.org/cddl-gplv2.html
014: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
015: * specific language governing permissions and limitations under the
016: * License. When distributing the software, include this License Header
017: * Notice in each file and include the License file at
018: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
019: * particular file as subject to the "Classpath" exception as provided
020: * by Sun in the GPL Version 2 section of the License file that
021: * accompanied this code. If applicable, add the following below the
022: * License Header, with the fields enclosed by brackets [] replaced by
023: * your own identifying information:
024: * "Portions Copyrighted [year] [name of copyright owner]"
025: *
026: * If you wish your version of this file to be governed by only the CDDL
027: * or only the GPL Version 2, indicate your decision by adding
028: * "[Contributor] elects to include this software in this distribution
029: * under the [CDDL or GPL Version 2] license." If you do not indicate a
030: * single choice of license, a recipient has the option to distribute
031: * your version of this file under either the CDDL, the GPL Version 2 or
032: * to extend the choice of license to its licensees as provided above.
033: * However, if you add GPL Version 2 code and therefore, elected the GPL
034: * Version 2 license, then the option applies only if the new code is
035: * made subject to such option by the copyright holder.
036: *
037: * Contributor(s):
038: *
039: * Portions Copyrighted 2007 Sun Microsystems, Inc.
040: */
041: import com.sun.data.provider.DataListener;
042: import com.sun.data.provider.DataProvider;
043: import com.sun.data.provider.FieldKey;
044: import com.sun.data.provider.RowKey;
045: import com.sun.data.provider.TableCursorListener;
046: import com.sun.data.provider.TableCursorVetoException;
047: import com.sun.data.provider.TableDataListener;
048: import com.sun.data.provider.TableDataProvider;
049: import com.sun.data.provider.impl.IndexRowKey;
050: import com.sun.data.provider.impl.ObjectArrayDataProvider;
051: import java.io.ByteArrayInputStream;
052: import java.io.ByteArrayOutputStream;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055: import java.util.List;
056: import java.util.Map;
057: import org.netbeans.junit.NbTestCase;
058:
059: /**
060: *
061: * @author winstonp
062: */
063: public class ObjectArrayDataProviderTest extends NbTestCase {
064:
065: public ObjectArrayDataProviderTest(String testName) {
066: super (testName);
067:
068: }
069:
070: @Override
071: protected void setUp() throws Exception {
072: super .setUp();
073: beans = new TestBean[5];
074: for (int i = 0; i < beans.length; i++) {
075: beans[i] = new TestBean("test" + i);
076: }
077: dp = new ObjectArrayDataProvider(beans, true);
078:
079: }
080:
081: @Override
082: protected void tearDown() throws Exception {
083: super .tearDown();
084: dp = null;
085: beans = null;
086: listener = null;
087: tdcListener = null;
088: tdpListener = null;
089: }
090:
091: // ------------------------------------------------------ Instance Variables
092: /**
093: * <p>The beans being wrapped by the {@link DataProvider} under test.
094: */
095: private TestBean beans[] = null;
096: /**
097: * <p>The {@link DataProvider} instance under test.
098: */
099: private ObjectArrayDataProvider dp = null;
100: /**
101: * <p>Event listener for event testing.</p>
102: */
103: private Listener listener = null;
104: /**
105: * <p>Event listener for event testing.</p>
106: */
107: private MyCursorListener tdcListener = null;
108: /**
109: * <p>Event listener for event testing.</p>
110: */
111: private MyDataListener tdpListener = null;
112: // -------------------------------------------------------- Static Variables
113: // Dummy variables just to provide access to type information
114: private static int intArray[] = new int[0];
115: private static TestBean nestedArray[] = new TestBean[0];
116: /**
117: * <p>Descriptors for the set of fieldKeys we expect to be known.</p>
118: */
119: private static Descriptor[] fieldKeys = {
120: // Specific fieldKeys of this class
121: new Descriptor("public1", String.class, false,
122: "This is public1"),
123: new Descriptor("public2", Integer.class, false,
124: new Integer(8888)), };
125: /**
126: * <p>Descriptors for the set of properties we expect to be known.</p>
127: */
128: private static Descriptor[] properties = {
129: // Specific properties of this class
130: new Descriptor("booleanProperty", Boolean.class, false,
131: Boolean.TRUE),
132: new Descriptor("byteProperty", Byte.class, false, new Byte(
133: (byte) 123)),
134: new Descriptor("doubleProperty", Double.class, false,
135: new Double(654.321)),
136: new Descriptor("floatProperty", Float.class, false,
137: new Float((float) 123.45)),
138: new Descriptor("id", String.class, false, null),
139: new Descriptor("intArray", intArray.getClass(), false, null),
140: new Descriptor("intList", List.class, false, null),
141: new Descriptor("intProperty", Integer.class, false,
142: new Integer(1234)),
143: new Descriptor("longProperty", Long.class, false, new Long(
144: 54321)),
145: new Descriptor("nestedArray", nestedArray.getClass(),
146: false, null),
147: new Descriptor("nestedList", List.class, false, null),
148: new Descriptor("nestedMap", Map.class, false, null),
149: new Descriptor("nestedProperty", TestBean.class, false,
150: null),
151: new Descriptor("readOnly", String.class, true, null),
152: new Descriptor("shortProperty", Short.class, false,
153: new Short((short) 321)),
154: new Descriptor("stringProperty", String.class, false,
155: "This is a String"),
156: new Descriptor("nullString", String.class, true, null),
157: // Inherited from java.lang.Object
158: new Descriptor("class", Class.class, true, null), };
159: /**
160: * <p>Descriptors for updates that should be applied and tested.</p>
161: */
162: private static Update[] updates = {
163: // Updates to read-write properties
164: new Update("booleanProperty", Boolean.FALSE),
165: new Update("byteProperty", new Byte((byte) 213)),
166: new Update("doubleProperty", new Double(123.456)),
167: new Update("floatProperty", new Float((float) 111.22)),
168: new Update("intProperty", new Integer(23432)),
169: new Update("longProperty", new Long((long) 55555)),
170: new Update("shortProperty", new Short((short) 123)),
171: new Update("stringProperty", "Updated string value"),
172: // Public fieldKeys are read-write as well
173: new Update("public1", "revised String1 value"),
174: new Update("public2", new Integer(55555)), };
175:
176: // ------------------------------------------------- Individual Test Methods
177: /**
178: * <p>Check convenience methods on abstract base class that
179: * should still show through the concrete implementation.</p>
180: */
181: public void testBaseClassMethods() {
182:
183: // Operate on FieldKey or field identifier
184: for (int i = 0; i < properties.length; i++) {
185: String fieldId = properties[i].name;
186: FieldKey fieldKey = dp.getFieldKey(fieldId);
187: assertEquals("type(" + fieldId + ")", dp.getType(fieldKey),
188: dp.getType(fieldId));
189: assertEquals("value(" + fieldId + ")", dp
190: .getValue(fieldKey), dp.getValue(fieldId));
191: assertEquals("readOnly(" + fieldId + ")", dp
192: .isReadOnly(fieldKey), dp.isReadOnly(fieldId));
193: }
194:
195: }
196:
197: /**
198: * <p>Check for event propogation for basic DataProvider events.</p>
199: */
200: public void testEventsBasic() {
201:
202: assertNotNull(dp.getFieldKey("intProperty"));
203: assertNotNull(dp.getFieldKey("public1"));
204:
205: // Register a new listener and verify that it worked
206: listener = new Listener();
207: dp.addDataListener(listener);
208: DataListener listeners[] = dp.getDataListeners();
209: assertEquals(1, listeners.length);
210: assertTrue(listener == listeners[0]);
211:
212: // Make sure we log the update events correctly
213: dp.setValue(dp.getFieldKey("intProperty"), new Integer(23432));
214: dp.setValue(dp.getFieldKey("public1"), "new public1");
215: assertEquals(
216: "intProperty/1234/23432//public1/This is public1/new public1//",
217: listener.getLog());
218:
219: // Deregister the old listener and verify that it worked
220: dp.removeDataListener(listener);
221: listeners = dp.getDataListeners();
222: assertEquals(0, listeners.length);
223:
224: }
225:
226: /**
227: * <p>Check for event propogation for cursor changes.</p>
228: */
229: public void testEventsCursor() throws Exception {
230:
231: // Register a new listener and verify that it worked
232: tdcListener = new MyCursorListener();
233: dp.addTableCursorListener(tdcListener);
234: TableCursorListener listeners[] = dp.getTableCursorListeners();
235: assertEquals(1, listeners.length);
236: assertTrue(tdcListener == listeners[0]);
237:
238: // Make sure we log cursor change events correctly
239: assertEquals(new IndexRowKey(0), dp.getCursorRow());
240: dp.setCursorRow(new IndexRowKey(2));
241: assertEquals(new IndexRowKey(2), dp.getCursorRow());
242: dp.setCursorRow(new IndexRowKey(1));
243: assertEquals(new IndexRowKey(1), dp.getCursorRow());
244: assertEquals(
245: "cursorChanging/RowKey[0]/RowKey[2]//cursorChanged/RowKey[0]/RowKey[2]//"
246: + "cursorChanging/RowKey[2]/RowKey[1]//cursorChanged/RowKey[2]/RowKey[1]//",
247: tdcListener.getLog());
248:
249: // Make sure we can deal with vetos as well
250: tdcListener.clear();
251: tdcListener.setVeto(true);
252: try {
253: dp.setCursorRow(new IndexRowKey(3));
254: fail("Should have thrown TableCursorVetoException");
255: } catch (TableCursorVetoException e) {
256: ; // Expected result
257: }
258: assertEquals(new IndexRowKey(1), dp.getCursorRow());
259: assertEquals(
260: "cursorChanging/RowKey[1]/RowKey[3]//cursorVetoed/RowKey[1]/RowKey[3]//",
261: tdcListener.getLog());
262:
263: // Deregister the old listener and verify that it worked
264: dp.removeTableCursorListener(tdcListener);
265: listeners = dp.getTableCursorListeners();
266: assertEquals(0, listeners.length);
267:
268: }
269:
270: /**
271: * <p>Check for event propogation on random data changes.</p>
272: */
273: public void testEventsData() {
274:
275: // Register a new listener and verify that it worked
276: tdpListener = new MyDataListener();
277: dp.addTableDataListener(tdpListener);
278: TableDataListener listeners[] = dp.getTableDataListeners();
279: assertEquals(1, listeners.length);
280: assertTrue(tdpListener == listeners[0]);
281:
282: dp.cursorFirst();
283: // Make sure we log the update events correctly
284: dp.setValue(dp.getFieldKey("intProperty"), new Integer(23432)); // Change, so event expected
285: Object value = dp.getValue(dp.getFieldKey("stringProperty"));
286: dp.setValue(dp.getFieldKey("stringProperty"), value); // No change, so no event expected
287:
288: assertEquals("FieldKey[intProperty]/RowKey[0]/1234/23432//" + // Row-specific event
289: "FieldKey[intProperty]/1234/23432//", // Row-independent event
290: tdpListener.getLog());
291:
292: // We should get a provider change event too
293: tdpListener.clear();
294: dp.setArray(dp.getArray());
295: assertEquals("providerChanged//", tdpListener.getLog());
296:
297: // Deregister the old listener and verify that it worked
298: dp.removeTableDataListener(tdpListener);
299: listeners = dp.getTableDataListeners();
300: assertEquals(0, listeners.length);
301:
302: }
303:
304: /**
305: * <p>Check some things that should <strong>not</strong> work.</p>
306: */
307: public void testNegative() {
308:
309: // Access to unknown fieldKey/property
310: try {
311: dp.getFieldKey("unknown id value");
312: fail("Should have thrown IllegalArgumentException");
313: } catch (IllegalArgumentException e) {
314: ; // Expected result
315: }
316:
317: // Attempt to update a read only value
318: try {
319: dp.setValue(dp.getFieldKey("readOnly"), "xyz");
320: fail("Should have thrown IllegalStateException");
321: } catch (IllegalStateException e) {
322: ; // Expected result
323: }
324:
325: // Attempt to set value with an incorrect data type
326: try {
327: dp.setValue(dp.getFieldKey("intProperty"), "string value");
328: fail("Should have thrown IllegalArgumentException");
329: } catch (IllegalArgumentException e) {
330: ; // Expected result
331: }
332:
333: }
334:
335: /**
336: * <p>Ensure that access to public fields can be turned off.</p>
337: */
338: public void testNoFields() {
339:
340: dp = new ObjectArrayDataProvider(beans, false);
341: try {
342: dp.getFieldKey("public1");
343: fail("Should have thrown IllegalArgumentException");
344: } catch (IllegalArgumentException e) {
345: ; // Expected result
346: }
347: try {
348: dp.getFieldKey("public2");
349: fail("Should have thrown IllegalArgumentException");
350: } catch (IllegalArgumentException e) {
351: ; // Expected result
352: }
353:
354: // Check the available properties for expected characteristics
355: checkProperties();
356: checkExtras();
357:
358: }
359:
360: /**
361: * <p>Test a pristine instance.</p>
362: */
363: public void testPristine() throws Exception {
364:
365: assertTrue(dp.isIncludeFields());
366: assertEquals(beans.length, dp.getRowCount());
367:
368: // Check the available fieldKeys and properties for expected characteristics
369: for (int i = 0; i < beans.length; i++) {
370: dp.setCursorRow(dp.getRowKey("" + i));
371: assertEquals("test" + i, dp.getValue(dp.getFieldKey("id")));
372: checkFields();
373: checkProperties();
374: checkExtras();
375: }
376:
377: // Check random access on the id property
378: for (int i = 0; i < beans.length; i++) {
379: assertEquals("test" + i, dp.getValue(dp.getFieldKey("id"),
380: dp.getRowKey("" + i)));
381: }
382:
383: }
384:
385: /**
386: * <p>Test serializability of this data provider.</p>
387: */
388: public void testSerializable() throws Exception {
389:
390: ByteArrayOutputStream baos = new ByteArrayOutputStream();
391: ObjectOutputStream oos = new ObjectOutputStream(baos);
392: oos.writeObject(dp);
393: oos.close();
394: ByteArrayInputStream bais = new ByteArrayInputStream(baos
395: .toByteArray());
396: ObjectInputStream ois = new ObjectInputStream(bais);
397: dp = (ObjectArrayDataProvider) ois.readObject();
398: ois.close();
399:
400: testPristine();
401:
402: }
403:
404: /**
405: * <p>Test updates to updateable fieldKeys and properties.</p>
406: */
407: public void testUpdates() {
408:
409: // Do the easy cases
410: checkUpdates();
411:
412: }
413:
414: // --------------------------------------------------------- Support Methods
415: /**
416: * <p>Ensure that the array returned by <code>getFieldKeys()</code>
417: * does not include any keys that should not be there.</p>
418: */
419: private void checkExtras() {
420:
421: FieldKey keys[] = dp.getFieldKeys();
422: assertNotNull(keys);
423: for (int i = 0; i < keys.length; i++) {
424: String name = keys[i].getFieldId();
425: boolean found = false;
426: for (int j = 0; j < properties.length; j++) {
427: if (name.equals(properties[j].name)) {
428: found = true;
429: break;
430: }
431: }
432: if (!found && dp.isIncludeFields()) {
433: for (int j = 0; j < fieldKeys.length; j++) {
434: if (name.equals(fieldKeys[j].name)) {
435: found = true;
436: break;
437: }
438: }
439: }
440: assertTrue("Id '" + name + "' is valid", found);
441: }
442:
443: }
444:
445: /**
446: * <p>Ensure that all the expected fieldKeys are present and have
447: * the specified default values (where possible).</p>
448: */
449: private void checkFields() {
450:
451: FieldKey dk = null;
452: String name = null;
453: for (int i = 0; i < fieldKeys.length; i++) {
454: name = fieldKeys[i].name;
455: dk = dp.getFieldKey(name);
456: assertNotNull("FieldKey for '" + name + "'", dk);
457: assertTrue("Type for '" + name + "'", isAssignableFrom(dp
458: .getType(dk), fieldKeys[i].type));
459: assertEquals("ReadOnly for '" + name + "'",
460: fieldKeys[i].canSetValue, dp.isReadOnly(dk));
461: if (fieldKeys[i].defaultValue != null) {
462: assertEquals("Value for '" + name + "'",
463: fieldKeys[i].defaultValue, dp.getValue(dk));
464: }
465: }
466:
467: }
468:
469: /**
470: * <p>Ensure that all the expected properties are present and have
471: * the specified default values (where possible).</p>
472: */
473: private void checkProperties() {
474:
475: FieldKey dk = null;
476: String name = null;
477: for (int i = 0; i < properties.length; i++) {
478: name = properties[i].name;
479: dk = dp.getFieldKey(name);
480: assertNotNull("FieldKey for '" + name + "'", dk);
481: assertTrue("Type for '" + name + "'", isAssignableFrom(dp
482: .getType(dk), properties[i].type));
483: assertEquals("ReadOnly for '" + name + "'",
484: properties[i].canSetValue, dp.isReadOnly(dk));
485: if (properties[i].defaultValue != null) {
486: assertEquals("Value for '" + name + "'",
487: properties[i].defaultValue, dp.getValue(dk));
488: }
489: }
490:
491: }
492:
493: private boolean isAssignableFrom(Class to, Class from) {
494: if (to.isPrimitive()) {
495: if (to == Boolean.TYPE) {
496: to = Boolean.class;
497: } else if (to == Character.TYPE) {
498: to = Character.class;
499: } else if (to == Byte.TYPE) {
500: to = Byte.class;
501: } else if (to == Short.TYPE) {
502: to = Short.class;
503: } else if (to == Integer.TYPE) {
504: to = Integer.class;
505: } else if (to == Long.TYPE) {
506: to = Long.class;
507: } else if (to == Float.TYPE) {
508: to = Float.class;
509: } else if (to == Double.TYPE) {
510: to = Double.class;
511: }
512: }
513: return to.isAssignableFrom(from);
514: }
515:
516: /**
517: * <p>Ensure that we can update all the simple fieldKeys and properties
518: * that should be updatable by default.</p>
519: */
520: private void checkUpdates() {
521:
522: FieldKey dk = null;
523: String name = null;
524: for (int i = 0; i < updates.length; i++) {
525: name = updates[i].name;
526: try {
527: dp.setValue(dp.getFieldKey(name), updates[i].value);
528: } catch (Exception e) {
529: fail("Cannot set value for '" + name + "':" + e);
530: }
531: assertEquals("Updated value for '" + name + "'",
532: updates[i].value, dp.getValue(dp.getFieldKey(name)));
533: }
534:
535: }
536:
537: // Private class to describe the expected properties
538: static class Descriptor {
539:
540: public Descriptor(String name, Class type, boolean canSetValue,
541: Object defaultValue) {
542: this .name = name;
543: this .type = type;
544: this .canSetValue = canSetValue;
545: this .defaultValue = defaultValue;
546: }
547:
548: public String name;
549: public Class type;
550: public boolean canSetValue;
551: public Object defaultValue;
552: }
553:
554: // Private class to represent an event listener
555: static class Listener implements DataListener {
556:
557: String log = "";
558:
559: public String getLog() {
560: return this .log;
561: }
562:
563: public void clear() {
564: this .log = "";
565: }
566:
567: public void valueChanged(DataProvider dp, FieldKey dk,
568: Object oldValue, Object newValue) {
569: log += dk.getFieldId() + "/" + oldValue + "/" + newValue
570: + "//";
571: }
572:
573: public void providerChanged(DataProvider dp) {
574: log += "providerChanged//";
575: }
576: }
577:
578: // Private class to represent a TableDataProvider cursor listener
579: static class MyCursorListener implements TableCursorListener {
580:
581: boolean veto = false;
582: String log = "";
583:
584: public String getLog() {
585: return this .log;
586: }
587:
588: public void clear() {
589: this .log = "";
590: }
591:
592: public void cursorChanged(TableDataProvider dp, RowKey oldRow,
593: RowKey newRow) {
594: log += "cursorChanged/" + oldRow + "/" + newRow + "//";
595: }
596:
597: public void cursorChanging(TableDataProvider dp, RowKey oldRow,
598: RowKey newRow) throws TableCursorVetoException {
599: log += "cursorChanging/" + oldRow + "/" + newRow + "//";
600: if (veto) {
601: log += "cursorVetoed/" + oldRow + "/" + newRow + "//";
602: throw new TableCursorVetoException("No way, Jose");
603: }
604: }
605:
606: public boolean isVeto() {
607: return this .veto;
608: }
609:
610: public void setVeto(boolean veto) {
611: this .veto = veto;
612: }
613: }
614:
615: // Private class to represent a TableDataProvider event listener
616: class MyDataListener implements TableDataListener {
617:
618: String log = "";
619:
620: public String getLog() {
621: return this .log;
622: }
623:
624: public void clear() {
625: this .log = "";
626: }
627:
628: public void valueChanged(DataProvider dp, FieldKey fk,
629: Object oldValue, Object newValue) {
630: log += fk + "/" + oldValue + "/" + newValue + "//";
631: }
632:
633: public void providerChanged(DataProvider dp) {
634: log += "providerChanged//";
635: }
636:
637: public void rowAdded(TableDataProvider dp, RowKey rk) {
638: log += "rowAdded/" + rk + "//";
639: }
640:
641: public void rowRemoved(TableDataProvider dp, RowKey rk) {
642: log += "rowRemoved/" + rk + "//";
643: }
644:
645: public void valueChanged(TableDataProvider dp, FieldKey fk,
646: RowKey rk, Object oldValue, Object newValue) {
647: log += fk + "/" + rk + "/" + oldValue + "/" + newValue
648: + "//";
649: }
650: }
651:
652: // Private class to describe updates to be performed and checked
653: static class Update {
654:
655: public Update(String name, Object value) {
656: this .name = name;
657: this .value = value;
658: }
659:
660: public String name;
661: public Object value;
662: }
663: }
|