MattHicks.com

Programming on the Edge

Have Beans been holding us back?

Published by Matt Hicks under , , , , , , on Tuesday, June 30, 2009
I have been a Java developer for many years now and have always taken for granted some of the standards pushed on me from the beginning. Java standards such as basic Java Beans (aka POJOs) are supposed to make life easier by providing simple getters/setters that work with the private fields of your Object. For years I have neglected to challenge this as it seemed perfectly natural, and when I started to use Eclipse and could simply tell Eclipse to generate the getters and setters for my beans for me it made it that much easier to simply ignore all the extra code being created.

Unfortunately, over the years there have been some additional frustrations that have resulted from this approach as I wrote more code and attempted to do more complex things in my projects. As a developer trying to write more efficient and more elegant code these things have been a constant reminder that no language nor methodology is ever perfect.

The first frustration which I found myself struggling with as a new developer not using an IDE (that happily writes code for me) is the many lines of code to simply modify a variable. For example:


public class SimpleBean {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}


Notice that it takes up seven lines of actual code put a simple String into my bean and that's not counting the whitespace added to "pretty it up". Sure, as a noob coder it's tempting to do something like this:


public class SimpleBean {
public String name;
}


and be done with it. However, as a serious developer that's really not practical as it creates additional problems down the road when you want to add validation, remove the setter so it's a read-only field, notify something internally when the value changes, etc.

The next frustration I have with Java Beans is the fact absolutely EVERYTHING is a manual coding exercise. Take Java's Observer/Observable pattern for example. When I was beginning to learn Java I ran into the problem that I wanted to be notified when a value changed in one of my beans and stumbled upon a Observer/Observable tutorial and at first glance I thought, "Wow, this is really great and exactly what I want", until I realized in amazement they expect me to extend Observable and add an additional line of code to EVERY SINGLE setter call that I want to monitor. Come on, I was looking for abstraction to keep my beans dumb and simple, I don't want to add a bunch of extra lines of code to every method. Also, what if I'm already extending another class that I can't change? I'm sure many people might read this and think I'm just being nitpicky, and yes, I am, but I care a great deal about elegance in code and I was hoping for some sort of feature on my Field to "addChangeListener(...)" or something. Besides, every additional line of code is an added line that has to be filtered through when trying to read and debug and thus makes your code more error-prone and harder to manage.

Extending upon the previous frustration is bindings. There have been many a rant on the topic of bindings in Java and though there are many APIs and even a JSR set to solve the problem I have yet to see one do a good job. I myself tried to solve this with my MagicBeans project. There were many iterations of it, but the most elegant required AspectJ to monitor pointcuts on setter methods and notified listeners. It was a complete and elegant abstraction, but required your projects to be compiled with AspectJ which ultimately I abandoned as not worth the added pain.

Ultimately the Java Bean paradigm raises issues primarily in its requirement of the onus being on the developer to write any and all functionality. It disallows you to easily extend or reuse functionality in an Object-Oriented way because they are methods with statically typed fields they represent and though you can accomplish everything you might need to with beans they require a much greater commitment to custom code than should otherwise be expected of the developer in my opinion.

It is these things that have led me to believe there's a better solution, and I've settled on what I believe is the better choice, to which I will explain in-depth in my next post. :)

Java Media Components (JMC) in Swing

Published by Matt Hicks under , , , , , on Sunday, May 03, 2009
If you're like me and have been really excited by all the hype of JMC, but think of yourself as a Java developer and not a fly being beckoned to the newest scripting framework with its bright and illuminating glow (JavaFX that is) then JMC comes as something of a disappointment. After all, every article on the web referencing JMC seems to inseparably link JavaFX and JMC as if they absolutely *MUST* go together.

However, this is not the case. I've blogged before about the evils of JavaFX and how Java developers can leverage the functionality without selling your soul to the scripting demons:

http://www.matthicks.com/2009/02/i-hate-javafx-i-love-javafx.html

Someone else has taken this another step by taking the same approach for JMC:

http://nishimotz.com/2009/03/10/using-jmc-from-java/

Now, though what Takuya wrote is fully functional, I hate the idea of having to include JavaFX just to use JMC. After a bit of digging I have come to find what seems to be a hidden feature (maybe Sun just thinks the idea of someone using it outside of JavaFX is ridiculous and not worth mentioning?), the jmc.jar file you get with JavaFX has some interesting classes in it that you can take advantage of in your own Swing applications without any hackery at all:


package test;

import java.io.File;

import javax.swing.JFrame;

import com.sun.media.jmc.JMediaPlayer;

public class TestJMC {
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame("Testing JMC"); {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);

JMediaPlayer player = new JMediaPlayer(new File("trailer_1080p.mov").toURI()); {
frame.add(player);
}

frame.setVisible(true);
}
}
}


I hate it when people give references like this and don't give any mention of a test file that can be used along with it, so to let you know what I used, I simply grabbed the trailer for "Big Buck Bunny" here:

http://www.bigbuckbunny.org/index.php/trailer-page/

What's awesome is that any one of them should work fine. Now, the player controls kind of suck, but in that same package there are other, probably more useful Swing classes: JMediaPane and JMediaView.

One last note. You need to include the jmc.dll (or the correct native lib for your OS) file in your library path to be able to play or you'll get unfriendly errors.

Have fun being able to leverage full video playing in your Swing applications.

JavaFX Wrapper: jSeamless 2.0

Published by Matt Hicks under on Saturday, March 07, 2009
I made a post recently (I Hate JavaFX; I Love JavaFX) in which I did a preliminary investigation on the idea of using the JavaFX API directly in Java rather than relying on JavaFX Script to do the job. I used the Clock example I saw on a tutorial on Sun's site as a practical example of something already done in JavaFX that showed several of the features of JavaFX. The resulting code was rather hideous as the API was not designed to be used directly, but it did work.

My next step was to begin writing a wrapper around this ugly Java API to give an elegant API structure for developing Java applications that utilize the awesome functionality of JavaFX for those people that don't care to use the scripting language. After a few days of work I'm close to having an alpha build ready to release and decided to redo the Clock example using this wrapper to see how easily it could be done.

I give you the resulting code:


package org.jseamless.example;

import java.util.Calendar;

import org.jseamless.Container;
import org.jseamless.View;
import org.jseamless.core.HorizontalAlignment;
import org.jseamless.core.VerticalAlignment;
import org.jseamless.effect.DropShadow;
import org.jseamless.paint.Color;
import org.jseamless.resource.URLResource;
import org.jseamless.shape.Ellipse;
import org.jseamless.shape.Line;
import org.jseamless.shape.path.ArcTo;
import org.jseamless.shape.path.LineTo;
import org.jseamless.shape.path.MoveTo;
import org.jseamless.shape.path.Path;
import org.jseamless.ui.Image;
import org.jseamless.ui.Text;
import org.xjava.bean.properties.Binding;
import org.xjava.bean.properties.Property;
import org.xjava.delegates.MethodDelegate;
import org.xjava.operation.Operation;
import org.xjava.operation.OperationParser;
import org.xjava.work.DelegateWorkUnit;
import org.xjava.work.ThreadManager;

public class Clock extends Container {
private float radius;
private float centerX;
private float centerY;

private Calendar calendar;
private Property hours;
private Property minutes;
private Property seconds;

public Clock() {
// Initial setup
calendar = Calendar.getInstance();
radius = 77.0f;
centerX = 144.0f;
centerY = 144.0f;

hours = new Property(null, 0.0f);
minutes = new Property(null, 0.0f);
seconds = new Property(null, 0.0f);

nextTick();

size.fill();

Image image = new Image(); {
image.source.set(new URLResource(getClass().getClassLoader().getResource("resource/clock_background.png")));

children.add(image);
}

Container face = new Container(); {
face.translate.x.set(centerX);
face.translate.y.set(centerY);

// Every third hour
for (int i = 3; i <= 12; i += 3) {
Text t = new Text(); {
t.translate.x.set(-5.0f);
t.translate.y.set(5.0f);
t.fontSize.set(16);
t.text.set(String.valueOf(i));

float x = radius * ((i + 0) % 2 * (2 - i / 3));
float y = radius * ((i + 1) % 2 * (3 - i / 3));
t.location.set(x, y);

face.children.add(t);
}
}

// Black circle for the rest of the hours
for (int i = 1; i < 12; i++) {
if (i % 3 == 0) {
continue; // Don't show a circle on the items that have text
}

Ellipse ellipse = new Ellipse(); {
ellipse.rotation.set(30.0f * i);
ellipse.location.x.set(radius);
ellipse.location.x.align.set(HorizontalAlignment.CENTER);
ellipse.location.y.align.set(VerticalAlignment.MIDDLE);
ellipse.size.set(6.0f, 6.0f);
ellipse.fill.set(Color.BLACK);

face.children.add(ellipse);
}
}

// Center circles
Ellipse ellipse = new Ellipse(); {
ellipse.location.x.align.set(HorizontalAlignment.CENTER);
ellipse.location.y.align.set(VerticalAlignment.MIDDLE);
ellipse.size.set(10.0f, 10.0f);
ellipse.fill.set(Color.DARK_RED);

face.children.add(ellipse);
}
ellipse = new Ellipse(); {
ellipse.location.x.align.set(HorizontalAlignment.CENTER);
ellipse.location.y.align.set(VerticalAlignment.MIDDLE);
ellipse.size.set(6.0f, 6.0f);
ellipse.fill.set(Color.RED);

face.children.add(ellipse);
}

// Second hand
Line line = new Line(); {
DropShadow ds = new DropShadow(); {
ds.offsetY.set(3.0f);

line.effect.set(ds);
}
Binding b = line.rotation.bind(seconds); {
OperationParser p = new OperationParser("(seconds * 6.0) + 90.0"); {
p.setVariable("seconds", seconds);
}
Operation operation = p.generateOperation();
b.setOperation(operation);
}

line.endX.set(-radius - 3.0f);
line.stroke.set(Color.RED);
line.stroke.width.set(2.0f);

face.children.add(line);
}

// Hour hand
Path path = new Path(); {
DropShadow ds = new DropShadow(); {
ds.offsetY.set(3.0f);

path.effect.set(ds);
}
Binding b = path.rotation.bind(seconds); {
OperationParser p = new OperationParser("((hours + (minutes / 60.0)) * 30.0) - 90.0"); {
p.setVariable("hours", hours);
p.setVariable("minutes", minutes);
}
Operation operation = p.generateOperation();
b.setOperation(operation);
}
path.fill.set(Color.BLACK);

MoveTo e1 = new MoveTo(); {
e1.x.set(4.0f);
e1.y.set(4.0f);

path.elements.add(e1);
}
ArcTo e2 = new ArcTo(); {
e2.x.set(4.0f);
e2.y.set(-4.0f);
e2.radiusX.set(1.0f);
e2.radiusY.set(1.0f);

path.elements.add(e2);
}
LineTo e3 = new LineTo(); {
e3.x.set(radius - 15.0f);
e3.y.set(0.0f);

path.elements.add(e3);
}

face.children.add(path);
}

// Minute hand
path = new Path(); {
DropShadow ds = new DropShadow(); {
ds.offsetY.set(3.0f);

path.effect.set(ds);
}
Binding b = path.rotation.bind(seconds); {
OperationParser p = new OperationParser("(minutes * 6.0) - 90.0"); {
p.setVariable("minutes", minutes);
}
Operation operation = p.generateOperation();
b.setOperation(operation);
}

path.fill.set(Color.BLACK);

MoveTo e1 = new MoveTo(); {
e1.x.set(4.0f);
e1.y.set(4.0f);

path.elements.add(e1);
}
ArcTo e2 = new ArcTo(); {
e2.x.set(4.0f);
e2.y.set(-4.0f);
e2.radiusX.set(1.0f);
e2.radiusY.set(1.0f);

path.elements.add(e2);
}
LineTo e3 = new LineTo(); {
e3.x.set(radius);
e3.y.set(0.0f);

path.elements.add(e3);
}

face.children.add(path);
}

children.add(face);
}
}

public void start() throws InterruptedException {
while (true) {
nextTick();

Thread.sleep(100);
}
}

public void nextTick() {
calendar.setTimeInMillis(System.currentTimeMillis());

seconds.set((float)calendar.get(Calendar.SECOND));
minutes.set((float)calendar.get(Calendar.MINUTE));
hours.set((float)calendar.get(Calendar.HOUR));
}

public static void main(String[] args) throws Exception {
Clock clock = new Clock();
View view = View.createWindow(clock);
view.title.set("jSeamless 2.0 Clock Example");
view.size.set("800px", "600px");

ThreadManager.getInstance().addWork(new DelegateWorkUnit(MethodDelegate.create(clock, "start")));
}
}


It looks strikingly similar to the uglier direct calls, but has been cleaned up a lot. This is still just the first pass, so more will come, but there are a ton of additional features and standardization features that have been thrown in as well as work-arounds for the broken key event functionality and so on.

I will be posting more in the near future as development continues on this wrapper, but I have decided this is the next logical step for jSeamless, so jSeamless 2.0 will not end up being a true UI abstraction, but rather a powerful wrapper around JavaFX and Swing functionality with some icing on the top.

I Hate JavaFX; I Love JavaFX!

Published by Matt Hicks under , , , , , on Friday, February 27, 2009
I've posted a few places and gotten into more than a few rants publicly about how awful I think it is that Sun has been pushing JavaFX to the Java community. This has not been based on what JavaFX provides, because I think that's absolutely spectacular, but rather that they created a whole new language (JavaFX Script) that they force you into using rather than Java code itself. I've heard all the well reasoned arguments for why this is from the binding support to simplified UI design concepts, but as a professional ActionScript developer as well as a professional Java developer, I believe that using a language that stinks of ActionScript is a major step backwards for the Java community. However, like I said, JavaFX has done an absolutely wonderful job of adding to the UI power of the Java arsenal though, and I want to use it. Since I know that the JavaFX Script compiles into Java byte-code, I knew there had to be some level of access to the underlying API structure directly from Java, but nobody seems to know how or where to do it. After quite a bit of digging I stumbled upon these two pages:

http://blogs.sun.com/javafx/entry/how_to_use_javafx_in

http://forums.sun.com/thread.jspa?threadID=5354921&tstart=1

They looked pretty ugly to me, but put me on the track to actually using JavaFX directly in Java without scripting. I sort of combined those two together to come up with what I think is a little bit more concise example:


package test;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.sun.scenario.scenegraph.JSGPanel;
import com.sun.scenario.scenegraph.SGNode;

import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;

public class TestJFX {
public static void main(String[] args) throws Exception {
Group group = new Group(); {
}
Rectangle rect = new Rectangle(); {
rect.$width.setAsFloat(200.0f);
rect.$height.setAsFloat(200.0f);
rect.$fill.set(Color.$BLUE);
}
Text text = new Text(); {
text.$x.setAsFloat(20.0f);
text.$y.setAsFloat(20.0f);
text.$content.set("Greetings Earthling!");
text.$fill.set(Color.$WHITE);
}
group.$content.insert(rect);
group.$content.insert(text);
SGNode node = group.getSGGroup();
JSGPanel sgPanel = new JSGPanel();
sgPanel.setScene(node);

JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(sgPanel, BorderLayout.CENTER);

JFrame frame = new JFrame("Test JavaFX");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setSize(800, 600);
frame.setVisible(true);
}
}


This isn't the prettiest or most efficient way to code, but it's a start. Next I decided that I'd like to reproduce the Clock example that JavaFX shows here:

http://java.sun.com/javafx/1/tutorials/build-javafx-nb-app/

This gave me a much more practical run through JavaFX from the programmatic perspective and allowed me to verify that it is in fact possible to write a complete application using Java making calls to JavaFX:


package test;

import java.awt.BorderLayout;
import java.io.IOException;
import java.util.Calendar;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.sun.javafx.functions.Function0;
import com.sun.javafx.runtime.location.FloatBindingExpression;
import com.sun.javafx.runtime.location.FloatVariable;
import com.sun.scenario.scenegraph.JSGPanel;
import com.sun.scenario.scenegraph.SGNode;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.lang.Duration;
import javafx.scene.Group;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;

/**
* @author Matt Hicks (matt@matthicks.com)
*/
public class Clock {
private float radius;
private float centerX;
private float centerY;

private Calendar calendar;
private int hours;
private int minutes;
private int seconds;

private FloatVariable hoursVariable;
private FloatVariable minutesVariable;
private FloatVariable secondsVariable;

public Clock() throws IOException {
// Initial setup
calendar = Calendar.getInstance();
radius = 77;
centerX = 144;
centerY = 144;

nextTick();

// Build JavaFX clock
Group group = new Group(); {
ImageView imageView = new ImageView(); {
Image image = new Image(); {
image.$bufferedImage.set(ImageIO.read(getClass().getClassLoader().getResource("resource/clock_background.png")));
}
imageView.$image.set(image);

group.$content.insert(imageView);
}

Group face = new Group(); {
Translate translate = new Translate(); {
translate.$x.setAsFloat(centerX);
translate.$y.setAsFloat(centerY);
}
face.$transforms.insert(translate);

// Every third hour
for (int i = 3; i <= 12; i += 3) {
Text text = new Text(); {
translate = new Translate(); {
translate.$x.setAsFloat(-5.0f);
translate.$y.setAsFloat(5.0f);
}
text.$transforms.insert(translate);

text.$font.set(Font.font("Arial", 16));

text.$x.setAsFloat(radius * ((i + 0) % 2 * (2 - i / 3)));
text.$y.setAsFloat(radius * ((i + 1) % 2 * (3 - i / 3)));
text.$content.set(String.valueOf(i));
}
face.$content.insert(text);
}

// Black circle for the rest of the hours
for (int i = 1; i < 12; i++) {
if (i % 3 == 0) {
continue; // Don't show a circle on every third hour
}

Circle circle = new Circle(); {
Rotate rotate = new Rotate(); {
rotate.$angle.setAsFloat(30.0f * i);
}
circle.$transforms.insert(rotate);
circle.$centerX.setAsFloat(radius);
circle.$radius.setAsFloat(3.0f);
circle.$fill.set(Color.$BLACK);
}
face.$content.insert(circle);
}

// Center circles
Circle circle = new Circle(); {
circle.$radius.setAsFloat(5.0f);
circle.$fill.set(Color.$DARKRED);
}
face.$content.insert(circle);
circle = new Circle(); {
circle.$radius.setAsFloat(3.0f);
circle.$fill.set(Color.$RED);
}
face.$content.insert(circle);

// Second hand
Line line = new Line(); {
Rotate rotate = new Rotate(); {
FloatBindingExpression exp = new FloatBindingExpression() {
public float computeValue() {
return seconds * 6;
}
};
secondsVariable = FloatVariable.make(exp);
rotate.$angle.bind(false, secondsVariable);
}
line.$transforms.insert(rotate);

line.$endY.setAsFloat(-radius - 3.0f);
line.$strokeWidth.setAsFloat(2.0f);
line.$stroke.set(Color.$RED);
}
face.$content.insert(line);

// Hour hand
Path path = new Path(); {
Rotate rotate = new Rotate(); {
FloatBindingExpression exp = new FloatBindingExpression() {
public float computeValue() {
return (hours + minutes / 60) * 30 - 90;
}
};
hoursVariable = FloatVariable.make(exp);
rotate.$angle.bind(false, hoursVariable);
}
path.$transforms.insert(rotate);

path.$fill.set(Color.$BLACK);

MoveTo e1 = new MoveTo(); {
e1.$x.setAsFloat(4.0f);
e1.$y.setAsFloat(4.0f);
}
path.$elements.insert(e1);
ArcTo e2 = new ArcTo(); {
e2.$x.setAsFloat(4.0f);
e2.$y.setAsFloat(-4.0f);
e2.$radiusX.setAsFloat(1.0f);
e2.$radiusY.setAsFloat(1.0f);
}
path.$elements.insert(e2);
LineTo e3 = new LineTo(); {
e3.$x.setAsFloat(radius - 15.0f);
e3.$y.setAsFloat(0.0f);
}
path.$elements.insert(e3);
}
face.$content.insert(path);

// Minute hand
path = new Path(); {
Rotate rotate = new Rotate(); {
FloatBindingExpression exp = new FloatBindingExpression() {
public float computeValue() {
return minutes * 6 - 90;
}
};
minutesVariable = FloatVariable.make(exp);
rotate.$angle.bind(false, minutesVariable);
}
path.$transforms.insert(rotate);

path.$fill.set(Color.$BLACK);

MoveTo e1 = new MoveTo(); {
e1.$x.setAsFloat(4.0f);
e1.$y.setAsFloat(4.0f);
}
path.$elements.insert(e1);
ArcTo e2 = new ArcTo(); {
e2.$x.setAsFloat(4.0f);
e2.$y.setAsFloat(-4.0f);
e2.$radiusX.setAsFloat(1.0f);
e2.$radiusY.setAsFloat(1.0f);
}
path.$elements.insert(e2);
LineTo e3 = new LineTo(); {
e3.$x.setAsFloat(radius);
e3.$y.setAsFloat(0.0f);
}
path.$elements.insert(e3);
}
face.$content.insert(path);

group.$content.insert(face);
}
}

Timeline timeline = new Timeline(); {
timeline.$repeatCount.setAsFloat(Timeline.$INDEFINITE);

KeyFrame kf = new KeyFrame(); {
kf.$time.set(Duration.valueOf(1000.0f));
kf.$canSkip.set(true);
kf.$action.set(new Function0() {
public Void invoke() {
nextTick();
return null;
}
});
}
timeline.$keyFrames.insert(kf);
}

// Display in Swing
SGNode node = group.getSGGroup();
JSGPanel sgPanel = new JSGPanel();
sgPanel.setScene(node);

JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(sgPanel, BorderLayout.CENTER);

JFrame frame = new JFrame("JavaFX Clock Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setSize(800, 600);
frame.setVisible(true);

timeline.play();
}

public void nextTick() {
calendar.setTimeInMillis(System.currentTimeMillis());
seconds = calendar.get(Calendar.SECOND);
minutes = calendar.get(Calendar.MINUTE);
hours = calendar.get(Calendar.HOUR_OF_DAY);

if (secondsVariable != null) {
secondsVariable.invalidate();
minutesVariable.invalidate();
hoursVariable.invalidate();
}
}

public static void main(String[] args) throws Exception {
new Clock();
}
}


Now, I did make some slight alterations as the original example uses java.util.Date calls that are deprecated, so I updated to using Calendar instead. Also, I had a little trouble with the binding functionality and had to make calls to invalidate in the nextTick() method. Other than that I stayed quite true to the example and the code works quite nicely.

This is the first step in what I'm ultimately planning as a wrapper around Swing and JavaFX to make more powerful UIs without using a crappy scripting language. :)

See this re-output as an Applet:
http://captiveimagination.com/download/clock/clock.html

Flex WebBrowser Component

Published by Matt Hicks under , , , , , on Wednesday, February 18, 2009
I've been working on an acceptable solution to display web pages within my Flex applications and as any of you know that have done any research on it will find, it's frustrating how lacking Flex is in support for this. The ideal solution would be for Flex to provide a fully functional component that renders web pages utilizing the native web browser. Alas, it would seem that it's far too complicated for them to leverage the browser they are already running in. Instead, their solution is to create components that support just enough HTML to be completely annoying since you can't get anything really useful to display well. Alright, so enough of that, like most of my posts, I try to mix rant and useful information, so lets get to the latter.

Okay, so the first thing that I discovered is that the only way to display web pages within my Flex content was to use IFrames. There are other possibilities, but after investigation with each they all come up with major flaws (primarily centered around lacking functionality) and IFrame support seems to be the best solution out of a bunch of bad solutions.

Yes, I'm sure if you've spent any time looking into this you'll already know that there are plenty of people that have already written IFrame support components in Flex and they work moderately well. However, there are some major pitfalls they have not resolved that I find unacceptable:
  1. Clipping - Because IFrames float above the Flash context they float above EVERYTHING. So not only do not get proper clipping of an Alert or anything else appearing above the component, you don't get proper clipping when it's inside of a container with scrollbars. This extremely limits the usefulness of the component and makes it almost useless in many sites
  2. Size and location updates. So many of these implementations of IFrame support do such an awful job of properly sizing and adjusting the frame as the Flex container is adjusted.
  3. Multiple components on the same page. In many cases I would like to have several IFrames being displayed on the page at one time and very few of the implementations have support for multiple being displayed at any time.
Yes, all of those are valid, but there are solutions available for all but #1. For a while I wondered if it was even possible to solve, but have since proven otherwise. :)

The first hurdle was figuring out a way to clip IFrame content. This was actually a lot easier than I expected it to be. I simply create a DIV, use absolute positioning, overflow to hidden, and add an iframe to it using relative positioning. From there it's just a simple matter of updating the location and size of the "visible area" for the DIV and adjusting the IFrame within it to clip the relevant areas. This does leave us without a perfect solution as we can obviously only show a single rectangular clipping area, but it's a lot better than no clipping at all.

Here's the code for the webbrowser.js file:
// WebBrowser specific code
function verifyCreated(id) {
var div = document.getElementById('div' + id);
if (div == null) {
div = document.createElement('div');
div.setAttribute('id', 'div' + id);
div.style.position = 'absolute';
div.style.visibility = 'hidden';
div.style.overflow = 'hidden';
var body = document.getElementsByTagName("body")[0];
body.appendChild(div);
}
var iframe = document.getElementById('iframe' + id);
if (iframe == null) {
iframe = document.createElement('iframe');
iframe.setAttribute('id', 'iframe' + id)
iframe.style.position = 'relative';
iframe.style.backgroundColor = 'white';
iframe.style.borderStyle = 'none';
iframe.setAttribute('frameborder', '0');
div.appendChild(iframe);
}
return iframe;
}

function updateBrowser(id, x, y, width, height, clipX, clipY, clipWidth, clipHeight) {
if ((width <= 0) || (height <= 0)) {
hideBrowser(id);
return;
}
var iframe = verifyCreated(id);
iframe.style.left = -clipX;
iframe.style.top = -clipY;
iframe.style.width = width;
iframe.style.height = height;
iframe.style.display = '';

var div = document.getElementById('div' + id);
div.style.left = x + clipX;
div.style.top = y + clipY;
div.style.width = clipWidth;
div.style.height = clipHeight;
div.style.visibility = 'visible';
div.style.display = '';
}

function loadURL(id, url) {
var iframe = verifyCreated(id);
iframe.src = url;
}

function hideBrowser(id) {
var div = document.getElementById('div' + id);
div.style.visibility = 'hidden';
div.style.display = 'none';
}
So that solves the dilema of clipping. The next hurdle, which I was quite surprised was a hurdle at all, was the of determining the visible bounds of a Flex component. You see, I wrote a WebBrowser Flex component that represents the IFrame within Flex and receives all the events for changes to size, location, visibility, etc. and then relays them to the IFrame it manages. However, Flex seems to have absolutely no support for finding the visibles bounds on a Component. So, as my solution to most things, I wrote my own:

        public static function getVisibleBounds(component:UIComponent):Rectangle {
var r:Rectangle = new Rectangle();

var step:Number = 50;

var best:Rectangle = new Rectangle();

var yOffset:Number = 0;

// Find largest bounding area
do {
nextBounds(component, yOffset, step, r);
yOffset += r.y + r.height + step;

if (r.width * r.height > best.width * best.height) {
best.x = r.x;
best.y = r.y;
best.width = r.width;
best.height = r.height;
}
} while (r.x != -1);

// Expand bounds broadly
expandBounds(component, best, step, step);

// Expand bounds narrowly
expandBounds(component, best, 1, step);

component.graphics.clear();
component.graphics.beginFill(0xffffff);
component.graphics.drawRect(best.x, best.y, best.width, best.height);
component.graphics.endFill();

return best;
}

private static function expandBounds(component:UIComponent, r:Rectangle, step:int, stepJump:int):void {
// Look up
while (validateHorizontal(component, r.x, r.y - step, r.width, stepJump)) {
r.y -= step;
r.height += step;
}

// Look down
while (validateHorizontal(component, r.x, r.y + r.height + step, r.width, stepJump)) {
r.height += step;
}

// Look left
while (validateVertical(component, r.x - step, r.y, r.height, stepJump)) {
r.x -= step;
r.width += step;
}

// Look right
while (validateVertical(component, r.x + r.width + step, r.y, r.height, stepJump)) {
r.width += step;
}
}

private static function validateHorizontal(component:UIComponent, x:int, y:int, width:int, step:int):Boolean {
for (var i:int = x; i <= width; i += step) {
if (!isUnderPoint(component, i, y)) {
return false;
}
}
if (isUnderPoint(component, x, y)) {
if (isUnderPoint(component, x + width, y)) {
return true;
}
}
return false;
}

private static function validateVertical(component:UIComponent, x:int, y:int, height:int, step:int):Boolean {
for (var i:int = y; i <= height; i += step) {
if (!isUnderPoint(component, x, i)) {
return false;
}
}
if (isUnderPoint(component, x, y)) {
if (isUnderPoint(component, x, y + height)) {
return true;
}
}
return false;
}

private static function nextBounds(component:UIComponent, yOffset:Number, step:int, r:Rectangle):Rectangle {
r.x = -1;
r.y = -1;
r.width = -1;
r.height = 0;

var p:Point = findFirstVisible(component, yOffset, step);
if (p != null) {
r.x = p.x;
r.y = p.y;
for (var y:int = p.y + step; y <= component.height; y += step) {
var currentWidth:Number = 0;
for (var x:int = p.x + step; x <= component.width; x += step) {
if (isUnderPoint(component, x, y)) {
currentWidth += step;
}
}
if (r.width == -1) {
r.width = currentWidth;
} else if (r.width > currentWidth) {
return r;
}
r.height += step;
}
}

return r;
}

private static function findFirstVisible(component:UIComponent, yOffset:Number, step:int):Point {
for (var y:int = yOffset; y <= component.height; y += step) {
for (var x:int = 0; x <= component.width; x += step) {
if (isUnderPoint(component, x, y)) {
return new Point(x, y);
}
}
}
return null;
}

private static var underPoint:Point = new Point();
public static function isUnderPoint(component:UIComponent, localX:Number, localY:Number):Boolean {
underPoint.x = localX + 1;
underPoint.y = localY;
underPoint = component.localToGlobal(underPoint);
var a:Array = Application.application.stage.getObjectsUnderPoint(underPoint);
for (var i:int = a.length - 1; i >= 0; i--) {
var c:UIComponent = getComponent(a[i]);
if (c != null) {
if (c.mouseEnabled) {
return c == component;
}
}
}
return false;
}

public static function getComponent(obj:DisplayObject):UIComponent {
while (obj != null) {
if (obj is UIComponent) {
return UIComponent(obj);
}
obj = obj.parent;
}
return null;
}


Yes, that's an awful lot of code, but it does work, and it works quite well. I find the largest bounding visible rectangular area for the Component and return a Rectangle representing it. I work in large steps (50 pixels at a time) to increase the performance and then make minor adjustments at the end to correct for the margin of precision loss.

The only thing still necessary to post is the WebBrowser Flex component:

    import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.external.ExternalInterface;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Timer;

import mx.containers.Canvas;
import mx.core.Application;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.events.MoveEvent;
import mx.events.ResizeEvent;

public class WebBrowser extends Canvas {
private static var ID_GENERATOR:int = 0;

[Inspectable(defaultValue=null)]
private var pageUrl:String = null;
private var initted:Boolean = false;

private var changed:Boolean;
private var lastChanged:Number;
private var hidden:Boolean;

private var lastX:Number = -1;
private var lastY:Number = -1;
private var lastW:Number = -1;
private var lastH:Number = -1;

public function WebBrowser() {
super();
id = String(++ID_GENERATOR);
this.addEventListener(FlexEvent.CREATION_COMPLETE, onFlex, false, 0, true);

var timer:Timer = new Timer(15);
timer.addEventListener(TimerEvent.TIMER, update);
timer.start();

setStyle('borderThickness', 0);
clipContent = false;

changed = false;
hidden = true;

lastChanged = -1;
}

private function onEvent(event:Event):void {
if (event.target is UIComponent) {
if (event.target.owns(this)) {
if (event.type == 'remove') {
// Hide it
visible = false;
}
validateWindow();
}
}
}

private function onFlex(event:FlexEvent):void {
validateWindow();
if (pageUrl != null) ExternalInterface.call('loadURL', id, pageUrl);
systemManager.addEventListener(FlexEvent.HIDE, onEvent, true, 0, true);
systemManager.addEventListener(FlexEvent.SHOW, onEvent, true, 0, true);
systemManager.addEventListener(MoveEvent.MOVE, onEvent, true, 0, true);
systemManager.addEventListener(ResizeEvent.RESIZE, onEvent, true, 0, true);
systemManager.addEventListener(FlexEvent.REMOVE, onEvent, true, 0, true);
systemManager.addEventListener(FlexEvent.ADD, onEvent, true, 0, true);
initted = true;
}

private function onHide(event:FlexEvent):void {
ExternalInterface.call('hideBrowser', id);
}

private function isVisible():Boolean {
var obj:DisplayObjectContainer = this;
while (obj != Application.application) {
if (obj == null) {
// Removed from container
return false;
}
if (!obj.visible) {
return false;
}
obj = obj.parent;
}
return true;
}

public function validateWindow():void {
if (!isVisible()) {
onHide(null);
return;
}
changed = true;
}

private function update(evt:TimerEvent):void {
var time:Number = new Date().time;

var p:Point = new Point(0, 0);
p = localToGlobal(p);
if ((p.x == 0) && (p.y == 0)) {
return;
}
if ((lastX != p.x) || (lastY != p.y) || (lastW != width) || (lastH != height)) {
changed = true;
} else if (isVisible() == hidden) {
changed = true;
} else if ((lastChanged != -1) && (time - lastChanged > 500)) {
changed = true;
}

if (changed) {
changed = false;
lastChanged = time;

if (isVisible()) {
hidden = false;
lastX = p.x;
lastY = p.y;
lastW = width;
lastH = height;

var rect:Rectangle = FlexUtilities.getVisibleBounds(this);
ExternalInterface.call('updateBrowser', id, p.x, p.y, width, height, rect.x, rect.y, rect.width, rect.height);
} else {
hidden = true;
onHide(null);
}
}
}

public function set source(_pageUrl:String):void {
pageUrl = _pageUrl;
if (initted) {
ExternalInterface.call('loadURL', id, pageUrl);
}
}

[Bindable(event="changeUrl")]
public function get source():String {
return pageUrl;
}
}
Well, that does it. I hope you found this useful and proof that IFrame support can actually be done moderately well in Flex. The biggest problem I still have is that it can still be moderately slow, but that's an underlying flaw of the IFrame displaying in Flex and not something I can do anything about unfortunately.

Why Scala is AWESOME

Published by Matt Hicks under on Wednesday, September 24, 2008
Many people refer to me as a Java evangelist, Java super-freak, and many other terms that tend to suggest that any language other than Java is outside of the realm of consideration. Though true that I do my fair share of bashing of languages like Ruby or Groovy, it is because I really have longed for a new language to come out that is better than Java. I've been programming in Java for several years and though I love the language there are many flaws that don't seem to be getting fixed any time soon. I know no language is perfect, but of the options I've always felt Java comes the closest.

When I first learned about Scala I shrugged it off as another crappy scripting language, but recently it has been brought back to my attention when I realized that it's not a scripting language at all, but a fully language that gets compiled into byte-code the same way Java does. In fact, it gets compiled into byte-code that shares space with Java. For a Java developer this is quite appealing to be able to use a new language yet still make use of all my old APIs and conventions.

There are several amazing things about Scala that seem to pick up where Java falls short and runs with it. I will do my best to outline these features here as I'm learning the language to helpfully show these points to others and also for later reference for myself.

  1. Everything is an Object - This is particularly awesome in it gives true delegate support, something Java has been lacking forever. I can pass around methods as parameters and so much more.
  2. Native Support for XML - Though the ramifications of this feature can be some ugly code, when you want to work with XML in your applications being able to simply create a variable and reference direct XML without having to wrap it into a String it can be very helpful. This not only provides creation-time validation, but keeps you from having to escape quotes and nasty multi-line concatenation if you tried to do the same thing with a String. More useful than anything I think is the direct support to query data back out of XML.
  3. Overriding Operators - Reference back to point #1. Since everything is an Object even addition and subtraction is working with Objects. In Scala they have this cool idea to provide another syntax for making calls. For example, classically you would make a call to a method like:
    myObject.doSomething("somevalue")
    However, in Scala you can also call the same thing like this:
    myObject doSomething "somevalue"
    My first thought when I saw this is more shock than awe. One of my biggest concerns as a developer is the open-ended structure in languages that allow programmers that are either new to programming or lack the common sense to write sensible code to exploit open-ended structure to do evil things to make their code completely unreadable. However, when I realized their purpose in doing this, the awe finally set in. Since a number is also an Object when I say:
    3 + 5
    That is the same as writing:
    3.+(5)
    This means that + is actually a method that can be implemented so I can write pretty code to add two Objects together to a result.

Java Delegates

Published by Matt Hicks under , , , , on Tuesday, July 01, 2008
If you're searching the internet for the title of this post you'll see a lot of people ranting about how Java doesn't have Delegate support. Though I have been one of these people, and continue to be frustrated that there is no native solution, that is not the purpose of this post.

I write this to say I've created what I would consider to be a pretty clean alternative to native delegates in Java using reflection. I created this as a feature in jSeamless and have been using it heavily for a while, but after a few people asking about support in non-jSeamless applications for my friendly Delegate support I decided I would remove it from jSeamless and make it a stand-alone API that anyone can use.

The Delegate implementation I find myself using more than anything else is MethodDelegate. It provides the ability to reference a specific method on a specific object that can be invoked. For example: