ca.sqlpower.architect.swingui.TestPlayPen.java Source code

Java tutorial

Introduction

Here is the source code for ca.sqlpower.architect.swingui.TestPlayPen.java

Source

/*
 * Copyright (c) 2008, SQL Power Group Inc.
 *
 * This file is part of Power*Architect.
 *
 * Power*Architect is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Power*Architect is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
package ca.sqlpower.architect.swingui;

import java.awt.Color;
import java.awt.Font;
import java.awt.Point;
import java.beans.PropertyDescriptor;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import junit.framework.TestCase;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;

import sun.font.FontManager;
import ca.sqlpower.object.SPObject;
import ca.sqlpower.sql.DataSourceCollection;
import ca.sqlpower.sql.JDBCDataSource;
import ca.sqlpower.sqlobject.SQLColumn;
import ca.sqlpower.sqlobject.SQLDatabase;
import ca.sqlpower.sqlobject.SQLObjectException;
import ca.sqlpower.sqlobject.SQLRelationship;
import ca.sqlpower.sqlobject.SQLTable;
import ca.sqlpower.sqlobject.SQLTable.TransferStyles;
import ca.sqlpower.util.SQLPowerUtils;

public class TestPlayPen extends TestCase {
    ArchitectSwingSession session;
    private PlayPen pp;
    private SQLDatabase ppdb;

    private static Logger logger = Logger.getLogger(TestPlayPen.class);

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        TestingArchitectSwingSessionContext context = new TestingArchitectSwingSessionContext();
        session = context.createSession();
        pp = session.getPlayPen();
        ppdb = session.getTargetDatabase();

    }

    public void testUndoAddTable() throws SQLObjectException {
        SQLTable t = new SQLTable(ppdb, "test_me", "", "TABLE", true);

        TablePane tp = new TablePane(t, pp.getContentPane());
        ppdb.addChild(t);

        pp.addTablePane(tp, new Point(99, 98));

        // this isn't the point of the test, but adding the tablepane has to work!
        assertNotNull(pp.findTablePane(t));

        //Undo the add child and the move table pane
        session.getUndoManager().undo();

        assertNull(pp.findTablePane(t));
    }

    public void testRedoAddTable() throws SQLObjectException {
        SQLTable t = new SQLTable(ppdb, "test_me", "", "TABLE", true);

        TablePane tp = new TablePane(t, pp.getContentPane());

        final SPObject root = SQLPowerUtils.getAncestorList(ppdb).get(0);
        root.setMagicEnabled(false);
        root.begin("Testing transaction");
        ppdb.addChild(t);
        pp.addTablePane(tp, new Point(99, 98));
        root.commit();
        root.setMagicEnabled(true);

        // this isn't the point of the test, but adding the tablepane has to work!
        assertNotNull(ppdb.getTableByName("test_me"));
        assertNotNull(pp.findTablePane(t));
        //undo the add child and the move table pane
        System.out.println("Undo action is " + session.getUndoManager().getUndoPresentationName());
        session.getUndoManager().undo();
        System.out.println("After undo, undo action is " + session.getUndoManager().getUndoPresentationName());
        assertNull(ppdb.getTableByName("test_me"));
        assertNull(pp.findTablePane(t));
        // redo the add table and the move
        session.getUndoManager().redo();
        tp = pp.findTablePane(t);
        assertNotNull("Table pane didn't come back!", tp);
        assertEquals("Table came back, but in wrong location", new Point(99, 98), tp.getLocation());
    }

    /**
     * Description of the scenario: We are to reverse engineer two tables into the playpen.
     * There exists one relationship between the two tables. After the first reverse engineering
     * process, we reverse engineering the parent table again into the playpen. Now we shoud
     * expect 3 tables in total lying on playpen, and 1 relationship between the first two tables.
     */
    public void testImportTableCopyHijacksProperly() throws SQLObjectException {

        SQLDatabase sourceDB = new SQLDatabase();
        pp.getSession().getRootObject().addChild(sourceDB);
        SQLTable sourceParentTable = new SQLTable(sourceDB, true);
        sourceParentTable.setName("parent");
        sourceParentTable.addColumn(new SQLColumn(sourceParentTable, "key", Types.BOOLEAN, 1, 0));
        sourceParentTable.addToPK(sourceParentTable.getColumn(0));
        sourceDB.addChild(sourceParentTable);

        SQLTable sourceChildTable = new SQLTable(sourceDB, true);
        sourceChildTable.setName("child");
        sourceChildTable.addColumn(new SQLColumn(sourceChildTable, "key", Types.BOOLEAN, 1, 0));
        sourceDB.addChild(sourceChildTable);

        SQLRelationship sourceRel = new SQLRelationship();
        sourceRel.attachRelationship(sourceParentTable, sourceChildTable, true);

        pp.importTableCopy(sourceChildTable, new Point(10, 10),
                ASUtils.createDuplicateProperties(pp.getSession(), sourceChildTable));
        pp.importTableCopy(sourceParentTable, new Point(10, 10),
                ASUtils.createDuplicateProperties(pp.getSession(), sourceParentTable));
        pp.importTableCopy(sourceParentTable, new Point(10, 10),
                ASUtils.createDuplicateProperties(pp.getSession(), sourceParentTable));

        int relCount = 0;
        int tabCount = 0;
        int otherCount = 0;
        for (PlayPenComponent ppc : pp.getContentPane().getChildren()) {
            if (ppc instanceof Relationship) {
                relCount++;
            } else if (ppc instanceof TablePane) {
                tabCount++;
            } else {
                otherCount++;
            }
        }
        // The behaviour of the reverse engineering is slightly modified. When reverse engineer copy(ies)
        // of related tables to the playpen. The relationship is not pointed to the old tables.   
        assertEquals("Expected three tables in pp", 3, tabCount);
        assertEquals("Expected two relationships in pp", 1, relCount); //changed from 2 to 1
        assertEquals("Found junk in playpen", 0, otherCount);

        TablePane importedChild = pp.findTablePaneByName("child");
        assertEquals("Incorrect reference count on imported child col", 2,
                importedChild.getModel().getColumn(0).getReferenceCount()); //changed from 3 to 2
    }

    /**
     * Test to ensure that the self-referencing table gets imported properly into the PlayPen.
     * @throws Exception
     */
    public void testImportTableCopyOnSelfReferencingTable() throws Exception {
        SQLDatabase sourceDB = new SQLDatabase();
        pp.getSession().getRootObject().addChild(sourceDB);
        SQLTable table = new SQLTable(sourceDB, true);
        table.setName("self_ref");
        SQLColumn pkCol = new SQLColumn(table, "key", Types.INTEGER, 10, 0);
        table.addColumn(pkCol);
        table.addToPK(table.getColumn(0));
        SQLColumn fkCol = new SQLColumn(table, "self_ref_column", Types.INTEGER, 10, 0);
        table.addColumn(fkCol);

        SQLRelationship rel = new SQLRelationship();
        rel.attachRelationship(table, table, false);
        rel.addMapping(pkCol, fkCol);
        sourceDB.addChild(table);

        pp.importTableCopy(table, new Point(10, 10), ASUtils.createDuplicateProperties(pp.getSession(), table));

        int relCount = 0;
        int tabCount = 0;
        int otherCount = 0;
        for (PlayPenComponent ppc : pp.getContentPane().getChildren()) {
            if (ppc instanceof Relationship) {
                relCount++;
            } else if (ppc instanceof TablePane) {
                tabCount++;
            } else {
                otherCount++;
            }
        }
        assertEquals("Expected one table in pp", 1, tabCount);
        assertEquals("Expected one relationship in pp", 1, relCount);
        assertEquals("Found junk in playpen", 0, otherCount);
    }

    /**
     * Test to ensure that when importing two copies of a self-referencing table, the
     * correct number of relationships get added, and furthermore, the relationships
     * all point to the correct table.
     * @throws Exception
     */
    public void testImportTableCopyOnTwoCopiesOfSelfReferencingTable() throws Exception {
        SQLDatabase sourceDB = new SQLDatabase();
        pp.getSession().getRootObject().addChild(sourceDB);
        SQLTable table = new SQLTable(sourceDB, true);
        table.setName("self_ref");
        SQLColumn pkCol = new SQLColumn(table, "key", Types.INTEGER, 10, 0);
        table.addColumn(pkCol);
        table.addToPK(table.getColumn(0));
        SQLColumn fkCol = new SQLColumn(table, "self_ref_column", Types.INTEGER, 10, 0);
        table.addColumn(fkCol);

        SQLRelationship rel = new SQLRelationship();
        rel.attachRelationship(table, table, false);
        rel.addMapping(pkCol, fkCol);
        sourceDB.addChild(table);

        pp.importTableCopy(table, new Point(10, 10), ASUtils.createDuplicateProperties(pp.getSession(), table));
        pp.importTableCopy(table, new Point(30, 30), ASUtils.createDuplicateProperties(pp.getSession(), table));

        int relCount = 0;
        int tabCount = 0;
        int otherCount = 0;
        for (PlayPenComponent ppc : pp.getContentPane().getChildren()) {
            if (ppc instanceof Relationship) {
                relCount++;
            } else if (ppc instanceof TablePane) {
                tabCount++;
            } else {
                otherCount++;
            }
        }
        assertEquals("Expected two tables in pp", 2, tabCount);
        assertEquals("Expected two relationships in pp", 2, relCount);
        assertEquals("Found junk in playpen", 0, otherCount);

        for (SQLTable t : pp.getTables()) {
            List<SQLRelationship> exportedKeys = t.getExportedKeys();
            List<SQLRelationship> importedKeys = SQLRelationship.getExportedKeys(t.getImportedKeys());

            assertEquals("Expected only one exported key in table", 1, exportedKeys.size());
            assertEquals("Expected only one imported key in table", 1, importedKeys.size());

            SQLRelationship exportedKey = exportedKeys.get(0);
            SQLRelationship importedKey = importedKeys.get(0);

            assertEquals("Expected exported key PK and FK tables to be the same", exportedKey.getFkTable(),
                    exportedKey.getPkTable());
            assertEquals("Expected imported key PK and FK tables to be the same", importedKey.getFkTable(),
                    importedKey.getPkTable());
            assertEquals("Expected exported key and imported key tables to be the same", exportedKey.getPkTable(),
                    importedKey.getPkTable());
        }
    }

    /**
     * Checks that the properties of an instance from the copy constructor are equal to the original.
     * In the case of a mutable property, it also checks that they don't share the same instance.
     * 
     * @throws Exception
     */
    public void testCopyConstructor() throws Exception {
        List<PropertyDescriptor> settableProperties = Arrays
                .asList(PropertyUtils.getPropertyDescriptors(pp.getClass()));
        Set<String> copyIgnoreProperties = new HashSet<String>();

        copyIgnoreProperties.add("UI");
        copyIgnoreProperties.add("UIClassID");
        copyIgnoreProperties.add("accessibleContext");
        copyIgnoreProperties.add("actionMap");
        copyIgnoreProperties.add("alignmentX");
        copyIgnoreProperties.add("alignmentY");
        copyIgnoreProperties.add("ancestorListeners");
        copyIgnoreProperties.add("autoscrolls");
        copyIgnoreProperties.add("border");
        copyIgnoreProperties.add("class");
        copyIgnoreProperties.add("component");
        copyIgnoreProperties.add("componentPopupMenu");
        copyIgnoreProperties.add("containerListeners");
        copyIgnoreProperties.add("contentPane");
        copyIgnoreProperties.add("cursorManager");
        copyIgnoreProperties.add("debugGraphicsOptions");
        copyIgnoreProperties.add("doubleBuffered");
        copyIgnoreProperties.add("enabled");
        copyIgnoreProperties.add("focusCycleRoot");
        copyIgnoreProperties.add("focusTraversalKeys");
        copyIgnoreProperties.add("focusTraversalPolicy");
        copyIgnoreProperties.add("focusTraversalPolicyProvider");
        copyIgnoreProperties.add("focusTraversalPolicySet");
        copyIgnoreProperties.add("focusable");
        copyIgnoreProperties.add("fontRenderContext");
        copyIgnoreProperties.add("graphics");
        copyIgnoreProperties.add("height");
        copyIgnoreProperties.add("ignoreTreeSelection");
        copyIgnoreProperties.add("inheritsPopupMenu");
        copyIgnoreProperties.add("inputMap");
        copyIgnoreProperties.add("inputVerifier");
        copyIgnoreProperties.add("insets");
        copyIgnoreProperties.add("layout");
        copyIgnoreProperties.add("managingFocus");
        copyIgnoreProperties.add("maximumSize");
        copyIgnoreProperties.add("minimumSize");
        copyIgnoreProperties.add("mouseMode");
        copyIgnoreProperties.add("name");
        copyIgnoreProperties.add("nextFocusableComponent");
        copyIgnoreProperties.add("opaque");
        copyIgnoreProperties.add("optimizedDrawingEnabled");
        copyIgnoreProperties.add("paintingEnabled");
        copyIgnoreProperties.add("paintingTile");
        copyIgnoreProperties.add("panel");
        copyIgnoreProperties.add("playPenContentPane");
        copyIgnoreProperties.add("preferredScrollableViewportSize");
        copyIgnoreProperties.add("preferredSize");
        copyIgnoreProperties.add("registeredKeyStrokes");
        copyIgnoreProperties.add("requestFocusEnabled");
        copyIgnoreProperties.add("rootPane");
        copyIgnoreProperties.add("scrollableTracksViewportHeight");
        copyIgnoreProperties.add("scrollableTracksViewportWidth");
        copyIgnoreProperties.add("selectedItems");
        copyIgnoreProperties.add("selectedRelationShips");
        copyIgnoreProperties.add("selectedTables");
        copyIgnoreProperties.add("session");
        copyIgnoreProperties.add("topLevelAncestor");
        copyIgnoreProperties.add("toolTipText");
        copyIgnoreProperties.add("transferHandler");
        copyIgnoreProperties.add("usedArea");
        copyIgnoreProperties.add("validateRoot");
        copyIgnoreProperties.add("verifyInputWhenFocusTarget");
        copyIgnoreProperties.add("vetoableChangeListeners");
        copyIgnoreProperties.add("viewPosition");
        copyIgnoreProperties.add("viewportSize");
        copyIgnoreProperties.add("visible");
        copyIgnoreProperties.add("visibleRect");
        copyIgnoreProperties.add("width");
        copyIgnoreProperties.add("x");
        copyIgnoreProperties.add("y");

        copyIgnoreProperties.add("draggingTablePanes");
        copyIgnoreProperties.add("rubberBand");

        // These should not be copied because any new PlayPen needs
        // different values or else it will not work on the new
        // PlayPen.
        copyIgnoreProperties.add("mouseZoomInAction");
        copyIgnoreProperties.add("mouseZoomOutAction");
        copyIgnoreProperties.add("scrollPane");

        // we're not sure if zoom should be duplicated...
        // it might mess up printing?!?!?
        copyIgnoreProperties.add("zoom");

        // individual lists (e.g. tables) checked instead
        copyIgnoreProperties.add("components");

        // The copy of the play pen is for things like print preview, so we don't want to
        // duplicate menus and other interactive features. (?)
        copyIgnoreProperties.add("popupFactory");

        //This property is specific to each play pen and it's settings will be re-calculated
        //regularly so it does not need to be copied.
        copyIgnoreProperties.add("criticismBucket");

        // First pass: set all settable properties, because testing the duplication of
        //             an object with all its properties at their defaults is not a
        //             very convincing test of duplication!
        for (PropertyDescriptor property : settableProperties) {
            if (copyIgnoreProperties.contains(property.getName()))
                continue;
            Object oldVal;
            try {
                oldVal = PropertyUtils.getSimpleProperty(pp, property.getName());
                // check for a setter
                if (property.getWriteMethod() != null) {
                    Object newVal = getNewDifferentValue(property, oldVal);
                    BeanUtils.copyProperty(pp, property.getName(), newVal);
                }
            } catch (NoSuchMethodException e) {
                logger.warn(
                        "Skipping non-settable property " + property.getName() + " on " + pp.getClass().getName());
            }
        }
        // Second pass get a copy make sure all of 
        // the original mutable objects returned from getters are different
        // between the two objects, but have the same values. 
        PlayPen duplicate = new PlayPen(pp.getSession(), pp);
        for (PropertyDescriptor property : settableProperties) {
            logger.info(property.getName() + property.getDisplayName() + property.getShortDescription());
            if (copyIgnoreProperties.contains(property.getName()))
                continue;
            Object oldVal;
            try {
                oldVal = PropertyUtils.getSimpleProperty(pp, property.getName());
                Object copyVal = PropertyUtils.getSimpleProperty(duplicate, property.getName());
                if (oldVal == null) {
                    throw new NullPointerException("We forgot to set " + property.getName());
                } else {
                    assertEquals("The two values for property " + property.getDisplayName() + " in "
                            + pp.getClass().getName() + " should be equal", oldVal, copyVal);

                    if (isPropertyInstanceMutable(property)) {
                        assertNotSame("Copy shares mutable property with original, property name: "
                                + property.getDisplayName(), copyVal, oldVal);
                    }
                }
            } catch (NoSuchMethodException e) {
                logger.warn(
                        "Skipping non-settable property " + property.getName() + " on " + pp.getClass().getName());
            }
        }
    }

    /**
     * This tests that copying and pasting a new table into
     * the play pen works. This also confirms that the new table
     * copied is not the source of the columns.
     */
    public void testPasteNewTable() throws Exception {
        SQLTable table = new SQLTable(null, "NewTable", "Remarks", "TABLE", true);
        table.addColumn(new SQLColumn(table, "NewCol1", Types.VARCHAR, 50, 0));
        table.addColumn(new SQLColumn(table, "NewCol2", Types.NUMERIC, 50, 0));

        DuplicateProperties duplicateProperties = ASUtils.createDuplicateProperties(pp.getSession(), table);
        duplicateProperties.setDefaultTransferStyle(TransferStyles.COPY);
        pp.importTableCopy(table, new Point(0, 0), duplicateProperties);
        assertEquals(1, pp.getTables().size());
        SQLTable copy = pp.getTables().get(0);
        assertEquals("NewTable", copy.getName());
        assertEquals(2, copy.getColumns().size());
        assertTrue(copy.getColumnByName("NewCol1") != null);
        assertTrue(copy.getColumnByName("NewCol2") != null);
        assertNull(copy.getColumnByName("NewCol1").getSourceColumn());
        assertNull(copy.getColumnByName("NewCol2").getSourceColumn());
    }

    /**
     * This tests that copying and pasting a table with source information into
     * the play pen works. This also confirms that the new table copied has the
     * same sources as the original table and does not use the table copied from
     * as its source. Note that this is testing copy, not reverse engineering.
     */
    public void testPasteCopyTable() throws Exception {
        SQLDatabase db = new SQLDatabase();
        pp.getSession().getRootObject().addChild(db);
        SQLColumn sourceCol = new SQLColumn();
        SQLTable sourceTable = new SQLTable(db, true);
        sourceTable.addColumn(sourceCol);
        SQLTable table = new SQLTable(db, "NewTable", "Remarks", "TABLE", true);
        final SQLColumn col1 = new SQLColumn(table, "NewCol1", Types.VARCHAR, 50, 0);
        col1.setSourceColumn(sourceCol);
        table.addColumn(col1);
        final SQLColumn col2 = new SQLColumn(table, "NewCol2", Types.NUMERIC, 50, 0);
        col2.setSourceColumn(sourceCol);
        table.addColumn(col2);

        DuplicateProperties duplicateProperties = ASUtils.createDuplicateProperties(pp.getSession(), table);
        duplicateProperties.setDefaultTransferStyle(TransferStyles.COPY);
        pp.importTableCopy(table, new Point(0, 0), duplicateProperties);
        assertEquals(1, pp.getTables().size());
        SQLTable copy = pp.getTables().get(0);
        assertEquals("NewTable", copy.getName());
        assertEquals(2, copy.getColumns().size());
        assertTrue(copy.getColumnByName("NewCol1") != null);
        assertTrue(copy.getColumnByName("NewCol2") != null);
        assertEquals(sourceCol, copy.getColumnByName("NewCol1").getSourceColumn());
        assertEquals(sourceCol, copy.getColumnByName("NewCol2").getSourceColumn());
    }

    /**
     * This tests that copying and pasting a table with source information into
     * the play pen works. This also confirms that the new table copied uses
     * the table reverse engineered from as its source.
     */
    public void testPasteReverseEngineeredTable() throws Exception {
        SQLDatabase db = new SQLDatabase();
        pp.getSession().getRootObject().addChild(db);
        SQLColumn sourceCol = new SQLColumn();
        SQLTable sourceTable = new SQLTable(db, true);
        sourceTable.addColumn(sourceCol);
        SQLTable table = new SQLTable(db, "NewTable", "Remarks", "TABLE", true);
        final SQLColumn col1 = new SQLColumn(table, "NewCol1", Types.VARCHAR, 50, 0);
        col1.setSourceColumn(sourceCol);
        table.addColumn(col1);
        final SQLColumn col2 = new SQLColumn(table, "NewCol2", Types.NUMERIC, 50, 0);
        col2.setSourceColumn(sourceCol);
        table.addColumn(col2);

        DuplicateProperties duplicateProperties = ASUtils.createDuplicateProperties(pp.getSession(), table);
        pp.importTableCopy(table, new Point(0, 0), duplicateProperties);
        assertEquals(1, pp.getTables().size());
        SQLTable copy = pp.getTables().get(0);
        assertEquals("NewTable", copy.getName());
        assertEquals(2, copy.getColumns().size());
        assertTrue(copy.getColumnByName("NewCol1") != null);
        assertTrue(copy.getColumnByName("NewCol2") != null);
        assertEquals(col1, copy.getColumnByName("NewCol1").getSourceColumn());
        assertEquals(col2, copy.getColumnByName("NewCol2").getSourceColumn());
    }

    /**
     * This tests that copying and pasting a table from one session to another
     * session within the same context will add the data source to the new session
     * and add the table properly.
     */
    public void testPasteTableAcrossSessions() throws Exception {
        ArchitectSwingSessionContext context = pp.getSession().getContext();
        context.setPlDotIniPath("pl.regression.ini");
        DataSourceCollection<JDBCDataSource> pl = context.getPlDotIni();
        JDBCDataSource ds = pl.getDataSource("regression_test");
        Connection con = ds.createConnection();
        Statement stmt = con.createStatement();
        stmt.execute("Create table newtable (newcol1 varchar(50), newcol2 varchar(50))");
        stmt.close();
        con.close();

        SQLDatabase db = new SQLDatabase(ds);
        SQLTable table = db.getTableByName("newtable");

        //New play pen in same context
        PlayPen newPP = pp.getSession().getContext().createSession().getPlayPen();

        DuplicateProperties duplicateProperties = ASUtils.createDuplicateProperties(newPP.getSession(), table);
        newPP.importTableCopy(table, new Point(0, 0), duplicateProperties);
        assertEquals(1, newPP.getTables().size());
        SQLTable copy = newPP.getTables().get(0);
        assertEquals("NEWTABLE", copy.getName().toUpperCase());
        assertEquals(2, copy.getColumns().size());
        assertTrue(copy.getColumnByName("NewCol1") != null);
        assertTrue(copy.getColumnByName("NewCol2") != null);
        assertTrue(table.getColumnByName("newcol1") != copy.getColumnByName("NewCol1").getSourceColumn());
        assertTrue(table.getColumnByName("newcol2") != copy.getColumnByName("NewCol2").getSourceColumn());
    }

    /**
     * Returns a new value that is not equal to oldVal. The
     * returned object will be a new instance compatible with oldVal.  
     * 
     * @param property The property that should be modified.
     * @param oldVal The existing value of the property to modify.  The returned value
     * will not equal this one at the time this method was first called.
     */
    private Object getNewDifferentValue(PropertyDescriptor property, Object oldVal) {
        Object newVal; // don't init here so compiler can warn if the
        // following code doesn't always give it a value
        if (property.getPropertyType() == String.class) {
            newVal = "new " + oldVal;
        } else if (property.getPropertyType() == Boolean.class || property.getPropertyType() == Boolean.TYPE) {
            if (oldVal == null) {
                newVal = new Boolean(false);
            } else {
                newVal = new Boolean(!((Boolean) oldVal).booleanValue());
            }
        } else if (property.getPropertyType() == Double.class || property.getPropertyType() == Double.TYPE) {
            if (oldVal == null) {
                newVal = new Double(0);
            } else {
                newVal = new Double(((Double) oldVal).doubleValue() + 1);
            }
        } else if (property.getPropertyType() == Integer.class || property.getPropertyType() == Integer.TYPE) {
            if (oldVal == null) {
                newVal = new Integer(0);
            } else {
                newVal = new Integer(((Integer) oldVal).intValue() + 1);
            }
        } else if (property.getPropertyType() == Color.class) {
            if (oldVal == null) {
                newVal = new Color(0xFAC157);
            } else {
                Color oldColor = (Color) oldVal;
                newVal = new Color((oldColor.getRGB() + 0xF00) % 0x1000000);
            }
        } else if (property.getPropertyType() == Font.class) {
            if (oldVal == null) {
                newVal = FontManager.getDefaultPhysicalFont();
            } else {
                Font oldFont = (Font) oldVal;
                newVal = new Font(oldFont.getFontName(), oldFont.getSize() + 2, oldFont.getStyle());
            }
        } else if (property.getPropertyType() == Set.class) {
            newVal = new HashSet();
            ((Set) newVal).add("test");
        } else if (property.getPropertyType() == List.class) {
            newVal = new ArrayList();
            ((List) newVal).add("test");
        } else if (property.getPropertyType() == Point.class) {
            newVal = new Point(1, 3);
        } else {
            throw new RuntimeException("This test case lacks a value for " + property.getName() + " (type "
                    + property.getPropertyType().getName() + ") in getNewDifferentValue()");
        }

        return newVal;
    }

    /**
      * Returns true if an instance of the given property type is of a mutable class.
      * Throws an exception if it lacks a case for the given type.
      * 
      * @param property The property that should be checked for mutability.
      */
    private boolean isPropertyInstanceMutable(PropertyDescriptor property) {
        if (property.getPropertyType() == String.class) {
            return false;
        } else if (Enum.class.isAssignableFrom(property.getPropertyType())) {
            return false;
        } else if (property.getPropertyType() == Boolean.class || property.getPropertyType() == Boolean.TYPE) {
            return false;
        } else if (property.getPropertyType() == Double.class || property.getPropertyType() == Double.TYPE) {
            return false;
        } else if (property.getPropertyType() == Integer.class || property.getPropertyType() == Integer.TYPE) {
            return false;
        } else if (property.getPropertyType() == Color.class) {
            return false;
        } else if (property.getPropertyType() == Font.class) {
            return false;
        } else if (property.getPropertyType() == Set.class) {
            return true;
        } else if (property.getPropertyType() == List.class) {
            return true;
        } else if (property.getPropertyType() == Point.class) {
            return true;
        } else {
            throw new RuntimeException("This test case lacks a value for " + property.getName() + " (type "
                    + property.getPropertyType().getName() + ") in isPropertyInstanceMutable()");
        }
    }
}