Review of Flex/Flash Builder 4 – Defective By Design?

Adobe has released its new version of the Flex Builder, now renamed Flash Builder 4. This version is radically different from previous versions of Flex, introducing the new Spark architecture and theme support. While I’m pleased Adobe has finally added support for Eclipse 3.5, I’m disappointed with some of the new architecture changes that make doing simple things, such as skinning a button, now quite cumbersome.

1. Spark and Halo: Duplicate Code will be Flex’s downfall

The Spark framework introduces a new set of UI components that sit on top of mx, and in many cases, duplicate the logic. For example, you can now define an old Halo button <mx:button/> alongside a new Spark button <s:button/>, both within the same component. I predict this duplication of code will lead to confusing and muddled codebases. Some developers will use all Spark components, some will use all Halo components, and some will use an inconsistent mix of the two.

The idea of two different components with the same name existing within the same application worries me greatly. It would be like Sun releasing a new version of the String class in Java 8, and rather than updating the old String class you now had 2 distinct classes to choose from, both called String, with the package name determining which you use. Sometimes reinventing the wheel is a sign the developers were too lazy to merge their changes into the existing wheel.

2. Broken Migration Paths

When I attempted to migrate some of my Flex 3 applications into Flash Builder 4, I received a number of compiler errors. The first thing I needed to do was right click on the project and change the Theme to Halo (Flex 3-based theme), since it defaults to Spark. This fixed many, but not all, of the compiler issues. After fixing the remaining compiler issues by hand, I ran the application only to discover none of my buttons had text on them! I enabled “Use Flash Text Engine in MX components” which fixed some, but not all, of the button text, but there were still dozens, possibly hundreds, of UI issues that caused the application to look awful. Realizing migrating the entire application into a native Flex 4 application would take endless amount of hours, I reviewed the compiler options again and checked the “Use Flex 3 compatibility mode” option, which, again, fixed some, but not all, of all the issues. Even with Flex 3 compatibility mode enabled, the compiler still handled the following components differently than it had in Flex 3: drag/drop pop-up images, recognizing css text-align property on certain components, and adding padding on some components. With the compatibility mode enabled, the number of issues was drastically reduced to a few dozen issues, but this was at the cost of limiting the new features available in Flex 4.

3. Skinning a button: 5 lines of code now take 51 lines of code

Previously, skinning a button was just a matter of defining some images in a CSS file and telling the application to use this style. It was intuitive, quite similar to HTML/CSS so it was easy to pick up, and only took a few lines of code, such as the following:

.myButton{
	skin:Embed(source="/assets/menu.png");
	overSkin:Embed(source="/assets/menu_over.png"); 
	downSkin:Embed(source="/assets/menu_down.png");
}

Flex 4 now makes skinning extremely complicated. In order to accomplish the above example in Flex 4, you would need to define a custom skin class such as in the following example taken from here:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2010/03/24/using-a-bitmap-image-skin-in-a-spark-button-control-in-flex-4/ -->
<s:SparkSkin name="ImageButtonSkin"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
        minWidth="21" minHeight="21"
        alpha.disabled="0.5">
    <!-- states -->
    <s:states>
        <s:State name="up" />
        <s:State name="over" />
        <s:State name="down" />
        <s:State name="disabled" />
    </s:states>
 
    <!-- host component -->
    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata>
 
    <fx:Script fb:purpose="styling">
        <![CDATA[
            /* Define the skin elements that should not be colorized.
            For button, the graphics are colorized but the label is not. */
            static private const exclusions:Array = ["labelDisplay"];
 
            override public function get colorizeExclusions():Array {
                return exclusions;
            }
 
            override protected function initializationComplete():void {
                useChromeColor = true;
                super.initializationComplete();
            }
        ]]>
    </fx:Script>
 
    <s:BitmapImage source="@Embed('/assets/menu.png')"
            source.over="@Embed('/assets/menu_over.png')"
            source.down="@Embed('/assets/menu_down.png')"
            left="0" right="0" top="0" bottom="0" />
    <!-- layer 8: text -->
    <s:Label id="labelDisplay"
            textAlign="center"
            verticalAlign="middle"
            maxDisplayedLines="1"
            horizontalCenter="0" verticalCenter="1"
            left="10" right="10" top="2" bottom="2" />
 
</s:SparkSkin>

Talk about wasted space! I understand the power the new skin mechanism adds (you can now layer effects), but I personally don’t feel it’s worth it.

The future of Flex

Adobe has touted the power of Flash Builder 4, but as we have all seen in the past sometimes more powerful designs lead to failure. I think Adobe forgot the KISS principle, or perhaps they had one too many designers at the table instead of developers when they were creating the new architecture. While I liked the ease, power, and simple nature of Flex 3, I’m not convinced of the merits of Flash Builder 4 from a developer perspective. It seems like they made our job a lot more difficult and time consuming.

More articles to follow as I delve into the beast that is Flash Builder 4!

clone a postgresql database for testing cleanly

I’m looking at writing integration tests for the back end of JavaRanch‘s JForum install.

A few “pesky” requirements/constraints

  • Multiple developers all over the word have their own local test databases filled with data in different states.  The tests must work for everyone.  Ideally they won’t leave data floating around either.
  • The tests must use PostgreSQL.  While the original JForum supported multiple databases, the JavaRanch version has been scaled down to just run with the one we need.  We do have some PostgreSQL specific SQL which rules out using an embedded database like HSQLDB or Derby.
  • Developers are using both Eclipse and IntelliJ.  Tests should care about the IDE anyway, so this isn’t a big constraint.
  • Developers are using a variety of operating systems and languages on their operating systems.  While code is in English, there can’t be assumptions as to the OS state.

Strategy

I think the best strategy is to create a second database just for testing.  The JForum database would remain untouched and a jforum_integration_test database can be created for the tests.  dbUnit can control the state of that special database.

The problem

Before I even start thinking about dbUnit, I did a proof of concept to ensure I could create a new database from scratch using the command line.  Creating a database is the easy part.  The “hard” part is that JForum doesn’t come with a schema.  It comes with an installation servlet that creates the schema.  While few people will be creating a schema for JForum, the technique I used applies elsewhere.

The procedure “before”

  1. Start up the JForum war
  2. Go to the JForum install URL and enter some information which creates the tables
  3. Run the JavaRanch customizations.

How to clone a database for which you only have a partial script

  1. Create an empty database
    createdb jforum_integration_test
  2. Arrive at the base schema
    1. Go the JForum installation URL
    2. Enter the information to create the tables
  3. Export the schema thus far
    pg_dump -U postgres jforum_integration_test > c:\temp\postgres.sql
  4. Provide instructions for the rest of the sql which were created by our developers.

How to import

Now for the easy part!

Importing this dump is a matter of a single command:

psql -U postgres jforum < "pathToWorkspace\JForum\javaranch-docs\deployment\file.ddl"

Lessons learned after

The next day I learned that this wasn’t enough.  We also needed some test data from the server.  I ran this a few times to get the relevant test data.

pg_dump --data-only --inserts -U user -W database --file roles  --table tableName

Conclusion

My next step will be to actually configure dbUnit against this new database and start writing tests.

Solution to Flex Image Rotation and Flipping around Center

Today, I was trying to rotate and flip an image around its center point in Flex and the solutions I came across on the web didn’t seem to do the trick. For images that have been previously dragged/dropped, resized, rotated, or translated, the existing solutions are simply not sufficient. With that in mind, I’ve written a solution for rotating and flipping an image around its center that works perfectly in all situations.

The Code

Without further ado, here’s the solution:

private static function rotateImage(image:Image, degrees:Number):void {
	// Calculate rotation and offsets
	var radians:Number = degrees * (Math.PI / 180.0);
	var offsetWidth:Number = image.contentWidth/2.0;
	var offsetHeight:Number =  image.contentHeight/2.0;

	// Perform rotation
	var matrix:Matrix = new Matrix();
	matrix.translate(-offsetWidth, -offsetHeight);
	matrix.rotate(radians);
	matrix.translate(+offsetWidth, +offsetHeight);
	matrix.concat(image.transform.matrix);
	image.transform.matrix = matrix;	
}

Also, here’s a related function to perform a horizontal (left to right) flip of an image:

private static function flipImage(image:Image):void {
	// Calculate offset
	var offsetWidth:Number = image.contentWidth/2.0;
	var offsetHeight:Number =  image.contentHeight/2.0;

	// Perform horizontal flip
	var matrix:Matrix = new Matrix();
	matrix.translate(-offsetWidth, -offsetHeight);
	matrix.scale(-1, 1);  // change to matrix.scale(1,-1) for vertical flip
	matrix.translate(+offsetWidth, +offsetHeight);
	matrix.concat(image.transform.matrix);
	image.transform.matrix = matrix;
}

The Problem Explained

The problem, in a nutshell, is that Flex maintains the top left corner of the image for all transformation and ignores the center point. For example, just calling the rotate() method will produce rotations around the corner such as in this example. Many almost-solutions, see here and here, resolve this by first moving (translating) the image around its center by using the image’s vertical and horizontal midpoint.

A more complex issue arises, though, when the image is no longer positioned at its anchor, or in technical speak the image’s transformation tx/ty values are not 0. I found once you sufficiently drag/drop, rotate, or otherwise modify the image, all of the solutions tend to fall apart. In my situation, all the images were oriented with tx/ty referring to the anchor of the canvas, making these previous solutions difficult to rely upon.

The Solution Explained

After some fun manipulating matrices, I’ve written and tested a solution that will properly rotate an existing image around its center regardless of where it is positioned on the canvas or what transformations have been performed on it. It starts by applying rotation around the image’s center to the identity matrix. In this way, it simulates rotating the matrix around its midpoint translated position, but it is actually applied to the identity matrix. Once the rotational matrix has been calculated, the image is stuck at the top of the canvas. What we really need is a matrix that will take the rotational matrix we’ve just calculated and apply all the previous transformations to orient/rotate the image in the proper position. Well it so happens we have such a matrix, the image’s transformation matrix in fact! So, we apply the transformation matrix of the image to our calculated rotational matrix and voila! The result is our transformed image rotated by the specified amount around its center. We just set the image’s transformation matrix to this result and we are done.