tag:blogger.com,1999:blog-67906096262958167192024-02-21T05:07:57.476-08:00Aberrant CodeCollin Fagan's blog dedicated to all things computery.Unknownnoreply@blogger.comBlogger16125tag:blogger.com,1999:blog-6790609626295816719.post-14321420514809735752015-01-15T15:19:00.004-08:002015-01-15T15:19:51.845-08:00Being clever to avoid being clever?I read an excellent article by Lukas Eder about the <a href="http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/">double curly braces anti pattern</a> in Java. I've never liked the double brace trick since it puts extra load on the classloader. Is there something we can do that gets us closer to what one might be tempted to do with the double brace syntax?
In java 8 we can try to use lambdas to give us a similar effect.
<code>
<pre>
Map<String, Object> source = Maps.from((map) -> {
map.put("firstName", "John");
map.put("lastName", "Smith");
map.put("organizations", Maps.from((orgs) -> {
orgs.put("0", Maps.pair("id", "1234"));
orgs.put("abc", Maps.pair("id", "5678"));
}));
});
</pre>
</code>
Still not as nice as if java had map literals like groovy does, but somewhat better.
<code>
<pre>
Groovy example:
def source = [
firstName: "John",
lastName: "Smith",
organizations: [
"0":[id:1234], "abc":[id:5678]
]
]
</pre>
</code>
Here is the actual code for the Maps class. What do people think? Too clever? Is there a better way?
<code>
<pre>
public static class Maps {
public static <K, V> Map<K, V> from(Consumer<Map<K, V>> target) {
HashMap<K, V> map = new HashMap<K, V>();
target.accept(map);
return map;
}
public static <K,V> Map<K,V> pair(K key, V value){
return Collections.singletonMap(key, value);
}
}
</pre>
</code>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-32173296139187337972014-08-27T19:11:00.000-07:002014-08-27T19:15:50.067-07:00Back to the Future!After a five year hiatus I'm committed to getting back to blogging. Expect to see Java, Arduino, design patterns and more in the future. Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-21273490134343420422008-12-09T08:02:00.001-08:002008-12-09T08:02:51.761-08:00CJUG Meeting on Tuesday, December 16th 2008Topic: The Seductions of Scala Scala is a new, statically-typed language for the JVM. Its advantages over Java include a succinct syntax, an improved object model and full support for functional programming (FP). In this code-heavy talk, I’ll show why Scala is so seductive, how it can improve your productivity, and why it might replace Java as the de facto language for the JVM.<br /><br />Who: Dean Wampler<br /><br />When: December 16th, 2008 6:00 PM - 8:00 PM (Networking from 6:00 PM - 6:30 PM) (Presentation from 6:30 PM - 8:00 PM)<br /><br />Where: Lewis Towers Ballroom, Beane Hall Loyola University of Chicago 820 N Michigan Ave (enter on Pearson 111 E. Pearson) Chicago, IL 60611<br /><br />Cost: FREE to current CJUG members and first time guests Join Now!<br /><br />For full details and to RSVP go to http://www.cjug.org/Wiki.jsp?page=2008.12.16.downtown<br /><br />Hope to see you there.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-41992182470170750602008-11-10T19:05:00.000-08:002008-11-16T10:23:33.499-08:00Layout Prototyping Part 1: XMLGUI Layout is hard, but is it harder then it should be in Java? Can we do something to better the situation? My next few blogs will be about prototyping different approaches in an attempt to find something better. For my first article I'm going to attempt to apply XML to the GUI layout. The goal is to approximate some of the features of MXML from Flex.<br /><br />In <a href="http://java.dzone.com/news/flex-layout-vs-java-layout-fir">a previous article</a> I compared layout logic in Flex/Flex Builder to Java. I found only one layout manager that had no Java equivalent. I wrote CoordinateLayout to mimic absolute layout from Flex. Here is a very basic example of it's usage.<br /><br /><pre><br />public class CoordinateLayoutDemo extends JPanel {<br /><br /> public CoordinateLayoutDemo() {<br /> setLayout(new CoordinateLayout());<br /><br /> add(new JLabel("Last Name:"), "x=10, y=10");<br /> add(new JTextField(), "x=87, y=8, width=223");<br /><br /> add(new JLabel("First Name:"), "x=318, y=10");<br /> add(new JTextField(), "x=395, y=10, width=207");<br /><br /> add(new JLabel("Phone:"), "x=10, y=36");<br /> add(new JTextField(), "x=87, y=34, width=223");<br /><br /> add(new JLabel("Email:"), "x=318, y=36");<br /> add(new JTextField(), "x=395, y=34, width=207");<br /><br /> add(new JLabel("Address:"), "x=10, y=62");<br /> add(new JTextField(), "x=87, y=60, width=515");<br /><br /> add(new JLabel("City:"), "x=10, y=88");<br /> add(new JTextField(), "x=87, y=86, width=223");<br /><br /> add(new JLabel("State"), "x=318, y=88");<br /> add(new JTextField(), "x=395, y=86, width=207");<br />}<br />}<br /></pre><br /><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzVzlXQcw8soK01tWlpTj66SifCgOaIuvmlrzfgaSwXEKnKqNk4T1lXbMNul_uanOyroz-9-PVzl2ZpeOgiYySg5x_5gtUcskLeSlgfcN5mgDAM7Gq1dlnLs2njydrpiQoIRQ2fiRyZf0/s320/cl1.png" alt="" id="BLOGGER_PHOTO_ID_5269269729975855458" border="0" /><br /><br />This lets one layout a screen pixel by pixel. It also has an "anchor mode" that allows one to determine how a component will resize by specifying the location of an anchor on one the four sides.<br /><br /><pre><br />public class CoordinateLayoutDemo2 extends JPanel {<br /> /**<br /> *<br /> */<br /> private static final long serialVersionUID = 5744676397576694341L;<br /><br /> public CoordinateLayoutDemo2() {<br /> setLayout(new CoordinateLayout());<br /><br /> add(new JScrollPane(new JTextArea()),<br /> "left=35, top=40, right=10, bottom=10");<br /> add(new JSlider(SwingConstants.VERTICAL), "bottom=10, left=10, top=40");<br /> add(new JComboBox(), "right=287, left=35, top=10");<br /> add(new JButton("Button"), "right=10, top=10");<br /> }<br />}<br /></pre><br /><br />This code results in the following screen.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz7B0BeJHsEXq0jjWdd-wSO8DGRUW3-qUM29qiicqvW1-5iU2A5uDYzqQRC5hdNuHMe2AcB2uHgGMAoWvJ2_5YwhUJ83EIILeb8gLEFSgkPd7T-Z4VjVpuO19_55yj1APszdYDkuh0adk/s1600-h/cl2.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 163px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz7B0BeJHsEXq0jjWdd-wSO8DGRUW3-qUM29qiicqvW1-5iU2A5uDYzqQRC5hdNuHMe2AcB2uHgGMAoWvJ2_5YwhUJ83EIILeb8gLEFSgkPd7T-Z4VjVpuO19_55yj1APszdYDkuh0adk/s320/cl2.png" alt="" id="BLOGGER_PHOTO_ID_5269274080399385954" border="0" /></a><br /><br />The only other thing I need is XML bindings and I have functionality similar to absolute layout.<br /><br /><a href="http://www.google.com/url?sa=t&source=web&ct=res&cd=1&url=http%3A%2F%2Fxmlbeans.apache.org%2F&ei=tD4gSfa3GZzoMp3pnc8J&usg=AFQjCNGN_wjo_uVxCn3dsrhwkz1WoLHcbA&sig2=7RLCA5MlEW0bWUR-TNxtdA">XMLBeans</a> to the Rescue. <br /><br />Without going into too much detail, XMLbeans takes an XSD (XML Schema Definition) and creates java classes that can parse XML that conforms to that schema.<br /><br />Here is a tiny schema that represents CoordinateLayout.<br /><br /><pre><br /><?xml version="1.0" encoding="utf-8" ?><br /><xs:schema elementFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"><br /> <xs:element name="CoordinateLayout"><br /> <xs:complexType><br /> <xs:sequence maxOccurs="unbounded"><br /> <xs:element name="item"><br /> <xs:complexType><br /> <xs:attribute name="name" type="xs:string" use="required" /><br /> <xs:attribute name="x" type="xs:int" /><br /> <xs:attribute name="y" type="xs:int" /><br /> <xs:attribute name="width" type="xs:string" /><br /> <xs:attribute name="height" type="xs:string" /><br /> <xs:attribute name="top" type="xs:int" /><br /> <xs:attribute name="left" type="xs:int" /><br /> <xs:attribute name="bottom" type="xs:int" /><br /> <xs:attribute name="right" type="xs:int" /><br /> <xs:attribute name="vcenter" type="xs:int" /><br /> <xs:attribute name="hcenter" type="xs:int" /><br /> </xs:complexType><br /> </xs:element><br /> </xs:sequence><br /> </xs:complexType><br /> </xs:element><br /></xs:schema><br /></pre><br />If I run this XSD through the XMLBeans schema compiler (scomp) I get a jar with classes that parse this schema. Now with a little drudgery to move the values from the schema generated objects to my layout I am able to represent my UI as an XML file. <br /><br />Here is an XML file that conforms the XSD and arranges the components the same way as the first example.<br /><pre><br /><?xml version="1.0" encoding="UTF-8"?><br /><CoordinateLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><br /> <item name="lastNameLabel" x="10" y="10" /><br /> <item name="lastName" x="87" y="8" width="223" /><br /><br /> <item name="firstNameLabel" x="318" y="10" /><br /> <item name="firstName" x="395" y="10" width="207" /><br /><br /> <item name="phoneLabel" x="10" y="36" /><br /> <item name="phone" x="87" y="34" width="223" /><br /><br /> <item name="emailLabel" x="318" y="36" /><br /> <item name="email" x="395" y="34" width="207" /><br /><br /> <item name="addressLabel" x="10" y="62" /><br /> <item name="address" x="87" y="60" width="515"/><br /><br /> <item name="cityLabel" x="10" y="88" /><br /> <item name="city" x="87" y="86" width="223"/><br /><br /> <item name="stateLabel" x="318" y="88"/><br /> <item name="state" x="395" y="86" width="207"/><br /></CoordinateLayout><br /></pre><br /><br />After all that I took a long hard look at my XML systems and asked myself two questions.<br /><br />1. Is this roughly equivalent to absolute layout from Flex?<br />2. Does this make layout any easier?<br /><br />The answer to number one is, yes. I consider it close enough to the the layout functionality of absolute layout. <br /><br />Number two, well no. XML doesn't help. No matter which way you slice it specifying pixel locations is not the simplest way of laying out components. <br /><br />Not wanting to abandon this XML idea I figured I'd apply it to other layout managers.<br /><br />The first one I tried was <a href="https://tablelayout.dev.java.net/">TableLayout</a>. I applied the same steps. <br /><br />1. Create an XML schema that represents the TableLayout finctionality.<br />2. Use XMLBeans to generate a jar file with the parsing code. <br />3. Glue the XMLBeans generated object to TableLayout. <br /><br />This allowed me to create the following XML file. <br /><br /><pre><br /><?xml version="1.0" encoding="utf-8"?><br /><tablelayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /> vgap="1" hgap="1"><br /><br /> <columnsizes><br /> <size>10</size><size>FILL</size><size>10</size><size>PREFERRED</size><br /> <size>10</size><size>PREFERRED</size><size>10</size><br /> </columnsizes><br /><br /> <rowsizes><br /> <size>10</size><size>PREFERRED</size><br /> <size>5</size><size>PREFERRED</size><size>10</size><br /> <size>PREFERRED</size><size>5</size><br /> <size>PREFERRED</size><size>10</size><br /> <size>PREFERRED</size><size>5</size><br /> <size>PREFERRED</size><size>10</size><br /> <size>PREFERRED</size><size>10</size><br /> </rowsizes><br /><br /> <item name="nameLabel" col1="1" row1="1" col2="5" row2="1" /><br /> <item name="name" col1="1" row1="3" col2="5" row2="3" /><br /><br /> <item name="addLabel" col1="1" row1="5" col2="5" row2="5" /><br /> <item name="address" col1="1" row1="7" col2="5" row2="7" /><br /><br /> <item name="cityLabel" col1="1" row1="9" /><br /> <item name="city" col1="1" row1="11" /><br /><br /> <item name="stateLabel" col1="3" row1="9" /><br /> <item name="state" col1="3" row1="11" /><br /><br /> <item name="zipLabel" col1="5" row1="9" /><br /> <item name="zip" col1="5" row1="11" /><br /><br /> <item name="button" col1="1" row1="13" col2="5" row2="13" /><br /></tablelayout><br /></pre><br /><br />Which results in this: <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio1sXlBOtwKOhH6K6wODm0TooSZ5AKZG0pnAxTrjBZHfnaRT4GF7uzjsBSDKDLMuPI7Hd8XsOAzO0Frw8akrkFY_w25v7hLFSKEAiUY1avc8m5Ql-bjEdXmLYkdxfmxElSZyBqSN_dNlc/s1600-h/tlx.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 127px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio1sXlBOtwKOhH6K6wODm0TooSZ5AKZG0pnAxTrjBZHfnaRT4GF7uzjsBSDKDLMuPI7Hd8XsOAzO0Frw8akrkFY_w25v7hLFSKEAiUY1avc8m5Ql-bjEdXmLYkdxfmxElSZyBqSN_dNlc/s320/tlx.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5269291547879402034" /></a><br /><br />I did the same thing with GridBagLayout. <br /><pre><br /><?xml version="1.0" encoding="utf-8"?><br /><gridbag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><br /> <defaults anchor="EAST" top="5" left="5" right="5" /><br /> <item name="lastNameLabel" /><br /> <item name="lastNameTF" fill="HORIZONTAL" weightx="1" /><br /> <item name="firstNameLabel" /><br /> <item name="firstNameTF" fill="HORIZONTAL" weightx="1" gridwidth="REMAINDER" /><br /> <item name="phone" /><br /> <item name="phoneTF" fill="HORIZONTAL" weightx="1" /><br /> <item name="email"/><br /> <item name="emailTF" fill="HORIZONTAL" weightx="1" gridwidth="REMAINDER" /><br /> <item name="address" /><br /> <item name="addressTF" fill="HORIZONTAL" weightx="1" gridwidth="REMAINDER" /><br /> <item name="city" bottom="5"/><br /> <item name="cityTF" fill="HORIZONTAL" weightx="1" bottom="5"/><br /> <item name="state" bottom="5"/><br /> <item name="stateTF" fill="HORIZONTAL" weightx="1" bottom="5"/><br /></gridbag><br /></pre><br /><br />Which actually looks like less code then writing this in Java. That's not hard though. Gridbag is rather cumbersome to use. <br /><br />I decided to go through this process once more. This time with <a href="http://www.miglayout.com/">MigLayout</a>. In the end I was disappointed. Not because it didn't work. Not because of any failure in MigLayout. Just because it was too simple. Now I could have built an XSD around the type-safe constraint objects available in MigLayout. Unfortunately that was going to make for some very verbose XML. Instead I just packaged up the string version of the constraints in some XML and called it a day. <br /><pre><br /><?xml version="1.0" encoding="utf-8"?><br /><miglayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /> layout="ins 20" column="[para]0[][100lp, fill][60lp][95lp, fill]" row=""><br /> <br /> <item name="man" params="skip" /><br /> <item name="manSep" params="span 3, growx, wrap" /><br /> <br /> <item name="compLbl" params="skip" /><br /> <item name="company" params="span, growx" /><br /> <item name="contactLabel" params="skip" /><br /> <item name="contact" params="span, growx" /><br /> <item name="orderLabel" params="skip" /><br /> <item name="order" params="wrap para" /><br /><br /> <item name="ins" params="skip" /><br /> <item name="insSep" params="span 3, growx, wrap" /><br /> <br /> <item name="namelabel" params="skip" /><br /> <item name="name" params="span, growx" /><br /> <item name="refNoLabel" params="skip" /><br /> <item name="refNo" params="wrap" /><br /> <item name="statLabel" params="skip" /><br /> <item name="stat" params="wrap para" /><br /> <br /> <item name="ship" params="skip" /><br /> <item name="shipSep" params="span 4, growx, wrap" /><br /> <br /> <item name="yardLabel" params="skip" /><br /> <item name="yard" params="span, growx" /><br /> <item name="regLabel" params="skip" /><br /> <item name="reg" params=""/><br /> <item name="hullLabel" params="right" /><br /> <item name="hull" params="wrap" /><br /> <item name="typeLabel" params="skip" /><br /> <item name="type" params=""/><br /></miglayout><br /></pre><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiVvaQKhYlYUQBY4DMQ-5zUW-ojRCt17x2cQo9ZB8NF2h1fdL3wbuLbkcNmtcqbOHvqfaJFjbI4-nwuqSNdostpxJ1NaSZWXvCAEExhWgP2gbmXdTtqvNVZU8oYP7aHf6bUJRkr_nTNO4/s1600-h/mig.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 292px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiVvaQKhYlYUQBY4DMQ-5zUW-ojRCt17x2cQo9ZB8NF2h1fdL3wbuLbkcNmtcqbOHvqfaJFjbI4-nwuqSNdostpxJ1NaSZWXvCAEExhWgP2gbmXdTtqvNVZU8oYP7aHf6bUJRkr_nTNO4/s320/mig.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5269301348224287218" /></a> <br />About five minuets after I finished I realized I created an XSD for what could be more simply represented as a properties file.<br /><br />Here is a properties file version of the same layout.<br /><pre>layout = ins 20 <br />column = [para]0[][100lp, fill][60lp][95lp, fill]<br />row = <br />man = skip<br />manSep = span 3, growx, wrap<br /> <br />compLbl = skip<br />company = span, growx<br />contactLabel = skip<br />contact = span, growx<br />orderLabel = skip<br />order = wrap para<br /><br />ins = skip<br />insSep = span 3, growx, wrap<br /> <br />namelabel = skip<br />name = span, growx<br />refNoLabel = skip<br />refNo = wrap<br />statLabel = skip<br />stat = wrap para<br /> <br />ship = skip<br />shipSep = span 4, growx, wrap<br /> <br />yardLabel = skip<br />yard = span, growx<br />regLabel = skip<br />reg =<br />hullLabel = right<br />hull = wrap<br />typeLabel = skip<br />type=<br /></pre><br /><br />So what did I accomplish? Well I can say I successfully built a system that decouples layout parameters from components. Certainly decoupling can be a good design pattern. Gridbag showed that there was some benefit to the XML way of doing things. Yet MigLayout showed that in the end it's all about the capability of the layout manager itself and not the markup. I don't think just applying XML is going to make GUI layout easier all by itself.<br /><br />I have a web start app demonstrating my prototype. <br /> <br /><a href="https://javatutorials.dev.java.net/FORUM/jnlp/XMLLayoutDemo.jnlp"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a><br /><br />The source is also available. <br /><br /><a href="https://javatutorials.dev.java.net/FORUM/zipfiles/xmllayoutdemo-src.zip">xmllayoutdemo-src.zip</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-63151767220916248012008-07-02T10:25:00.000-07:002008-07-02T10:26:46.168-07:00<p>Lately I've been spending my spare time contributing <a href="http://wiki.java.net/bin/view/Projects/Javatutorials">code samples</a> to the <a href="https://javatutorials.dev.java.net/">The Java Tutorial Community Portal</a>. My hope is that one or more of these examples will be interesting enough to be included in the official tutorial. My latest addition is an example on how to override tool tip generation for a particular component. This example is a tabbed pane that shows a preview of a tab as a tooltip. </p> <p><img src="https://javatutorials.dev.java.net/FORUM/images/ThumbnailTooltipDemo.png" /></p> <p>In this picture the mouse was hovering over the "images" tab.</p> <p>There are techniques for installing custom universal tool tip providers. In this case I only wanted to change the behaviour for one particular component. To accomplish this I overrode the createToolTip() method of JTabbedPane with the following: </p> <pre>/**<br />* overide createToolTip() to return a JThumbnailToolTip<br />*/<br />@Override<br />public JToolTip createToolTip() {<br /> Point loc = getMousePosition();<br /> int index = indexAtLocation(loc.x, loc.y);<br /><br /> if (index != -1) {<br /> Component comp = getComponentAt(index);<br /> <br /> BufferedImage image = ImageFactory.toImage(comp);<br /> <br /> JToolTip tip = new JImageToolTip(image, 0.5);<br /> <br /> return tip;<br /> } else {<br /> return super.createToolTip();<br /> }<br />}<br /></pre> <p>This method returns an instance of JImageToolTip which displays an image of the hidden component instead of the tooltip text. The image of the component was generated by the toImage() method of my ImageFactory class. </p> <pre>/**<br />* Generates a buffered Image of the specified component.<br />*<br />* @param componet<br />* @return image 'snapshot' of the component and it's state at the time this<br />* method was called.<br />*/<br />public static BufferedImage toImage(Component componet) {<br /> BufferedImage image = new BufferedImage(componet.getWidth(), componet<br /> .getHeight(), BufferedImage.TYPE_INT_RGB);<br /> Graphics graphics = image.getGraphics();<br /> graphics.setColor(componet.getBackground());<br /> graphics.fillRect(0, 0, componet.getWidth(), componet.getHeight());<br /> componet.paint(graphics);<br /> graphics.dispose();<br /><br /> return image;<br />}<br /><br /></pre> The JImageToolTipThis also class was used to create a label that displays a thumbnail of an image and provides the full size image as a tooltip. <p><img src="https://javatutorials.dev.java.net/FORUM/images/ThumbnailTooltipDemo2.png" /></p> <p>I've made the full source code available. Comments on this or any other <a href="http://wiki.java.net/bin/view/Projects/Javatutorials">code sample</a> are welcomed and encouraged. </p> <p> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/ThumbnailTooltipDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a> </p> <p> <a href="https://javatutorials.dev.java.net/FORUM/zipfiles/ThumbnailTooltipDemo-src.zip" rel="nofollow" target="_top">Source Code: ThumbnailTooltipDemo-src.zip</a> </p> Collator - Faster Comparison for identical strings.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-3253944182506525612008-06-03T19:11:00.000-07:002008-06-03T19:12:07.049-07:00Java Tutorial Community - Jtree and Borders Code samplesI've been working away on building "code samples" for the <a href="https://javatutorials.dev.java.net/">Java Tutorial Community Portal</a>. Each one is an example of a piece of code that at one time or another I've found useful. I'm not sure which ones will be interesting or even worthy of a full on tutorial. I'm just posting them and looking for feedback. I have four code samples today.<br /><br />1. <a href="http://wiki.java.net/bin/view/Projects/SwitchRootDemo">SwitchRootDemo</a> - Demonstrates how to change the root of a default tree model.<br /><p><img src="https://javatutorials.dev.java.net/FORUM/images/SwitchRootDemo.png" /> </p><p> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/SwitchRootDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a> </p>2. <a href="http://wiki.java.net/bin/view/Projects/NodeRolloverDemo">NodeRolloverDemo</a> - Demonstrates how to highlight a node in a tree when the mouse is over it.<br /><p><img src="https://javatutorials.dev.java.net/FORUM/images/NodeRolloverDemo.png" /> </p><p> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/NodeRolloverDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a> </p>3. FlexibleSearchTreeDemo - Demonstrates how to expand the searching functionality of JTree. Also has <a href="http://java.sun.com/docs/books/tutorial/essential/regex/" rel="nofollow" target="_top">RegEx</a> support.<br /><br /><p> <img src="https://javatutorials.dev.java.net/FORUM/images/FlexibleSearchTreeDemo.png" /> </p><p> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/FlexibleSearchTreeDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></p><p>4. <a href="http://wiki.java.net/bin/view/Projects/CustomBorderDemo">CustomBorderDemo</a> - Demonstrates how to use <span class="twikiNewLink">Java2D</span> to create a custom border. </p><p></p><p> <img src="https://javatutorials.dev.java.net/FORUM/images/CustomBorderDemo.png" /> </p><p> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/CustomBorderDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></p><p>I have a few other examples in the works for trees. I hope to post that in a few days.<br /></p><p><br /></p><p><br /> </p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-81525615860128451952008-05-19T08:42:00.000-07:002008-05-19T09:23:37.257-07:00Contributing to The Java Tutorials Community ProjectI have been working on contributing to the <a href="https://javatutorials.dev.java.net/">Java Tutorials Community Project</a>. I've put together some <a href="http://wiki.java.net/bin/view/Projects/Javatutorials">code samples</a> as potential tutorial subjects.<br /><ol><li><a href="http://wiki.java.net/bin/view/Projects/JTableColumnsDemo">JTable - ColumnsDemo</a> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/ColumnDemo.jnlp" rel="nofollow" target="_top"><img style="float: none;" src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></li><li><strong></strong><a href="http://wiki.java.net/bin/view/Projects/JTableColorEntireRowDemo">JTable - ColorEntireRowDemo</a> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/RowHighlightDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></li><br /><li><a href="http://wiki.java.net/bin/view/Projects/JTableMultiLineRows">JTable - MultiLineRows</a> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/MultiLineRowDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></li><br /><li><a href="http://wiki.java.net/bin/view/Projects/JTableFrozenColumns">JTable - FrozenColumns</a> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/NonScrollingColumnsDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></li><br /><li><a href="http://wiki.java.net/bin/view/Projects/DefaultTreeModelDemo">JTree - DefaultTreeModelDemo</a> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/DefaultTreeModelDemo.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></li><br /><li><a href="http://wiki.java.net/bin/view/Projects/CustomEditorDemo">JTree - CustomEditorDemo</a> <a href="https://javatutorials.dev.java.net/FORUM/jnlp/CustomEditorDemoPanel.jnlp" rel="nofollow" target="_top"><img src="https://javatutorials.dev.java.net/images/jws-launch-button.png" /></a></li></ol><br />The tutorials project has <a href="http://forums.java.net/jive/category.jspa?categoryID=65">forums</a> where you can comment on these proposed topics, propose new topics and to request revisions to any of the current Java tutorials. We need your help to create the kind of content that can help reduce the Java learning curve and help create better software.Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-6790609626295816719.post-35918781882848497382008-03-20T16:34:00.000-07:002008-03-21T05:47:01.907-07:00The secret life of a DefaultTreeModelThis is a draft of a small tutorial I have been attempting to submit to Sun for inclusion in the official Java tutorial. Unfortunately they are very busy lately and have not had the time to review my work. My intent is to bring to light some of the subtleties of the DefaulTreeModel API that are not covered in the Java tutorial on JTree.<br /><br />This tutorial will revolve around a demo application designed to show the difference between the right and wrong way to interact with DefaulTreeModel. Right clicking on any node in the tree will bring up a menu with a range of options. <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXj0wfSDfrfll-J0AAOe7a65MU60ZP4Ddkm8XC_EYMcf34Jhr258BrAjOaP0rV6p-rHkrWVSLLKh1EXONSdvsN_eBtvqeZ4d9Cq_sCWmDXbwtDlQFoJmC8Q-j2wCk5l8p4OOaU0gNtFws/s1600-h/TreeModelDemo1.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXj0wfSDfrfll-J0AAOe7a65MU60ZP4Ddkm8XC_EYMcf34Jhr258BrAjOaP0rV6p-rHkrWVSLLKh1EXONSdvsN_eBtvqeZ4d9Cq_sCWmDXbwtDlQFoJmC8Q-j2wCk5l8p4OOaU0gNtFws/s320/TreeModelDemo1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5180010472183203858" /></a><br /><br /><b>Adding and Removing nodes in a DefaultTreeModel. </b><br /><br />A DefaulTreeModel collects DefaultMutableTreeNodes together into a tree structure. At first glance the child modification methods like add() and remove() from DefaultMutableTreeNode look as if they overlap with the insertNodeInto() and removeNodeFromParent() from DefaulTreeModel. When the methods from DefaultTreeModel are used events are fired that update any JTree this model may be associated with. Simply modifying the DefaultMutableTreeNode does not. This can be demonstrated by the following chunk of code.<br /><br /><i>Example of the wrong way to remove a node.</i><br /><font face="Courier New">TreePath currentSelection = tree.getSelectionPath();<br />if (currentSelection != null) {<br /> DefaultMutableTreeNode node = (DefaultMutableTreeNode) <br /> currentSelection.getLastPathComponent();<br /> node.removeFromParent();<br />}</font><br /><br />Calls to methods like add() or remove() from DefaultMutableTreeNode need to be followed by a call to the DefaultTreeModel to notify the JTree of model changes. In our example a call to nodeStructureChanged() on the parent of the node being removed would have allowed the tree to be updated correctly. For adding or removing a single node from a tree the simplest solution is to call the insertNodeInto and removeNodeFromParent methods on DefaultTreeModel.<br /><br /><i>Example of the correct way to remove a node.</i><br /><font face="Courier New">TreePath currentSelection = tree.getSelectionPath();<br />if (currentSelection != null) {<br /> DefaultMutableTreeNode node = (DefaultMutableTreeNode)<br /> currentSelection.getLastPathComponent();<br /> DefaultTreeModel model = ((DefaultTreeModel) tree.getModel());<br /> model.removeNodeFromParent(node);<br />}</font><br /><br />There may be situations where multiple additions or removals happen in large blocks. In these cases the model may not need to be notified of a change until the bulk operation is completed. This is exemplified by the following code which adds multiple children to a node.<br /><br /><font face="Courier New">...<br />/**<br /> *<br /> * Adds 2 children, "even" and "odd". Even contains all the even numbers from zero to 49.<br /> * Odd contains all the odd numbers from zero to 49.<br /> */<br />TreePath currentSelection = tree.getSelectionPath();<br />if (currentSelection != null) {<br /> DefaultMutableTreeNode node = (DefaultMutableTreeNode)<br /> currentSelection.getLastPathComponent();<br /> DefaultTreeModel model = ((DefaultTreeModel) tree.getModel());<br /><br /> DefaultMutableTreeNode odd = new DefaultMutableTreeNode("odd");<br /> node.add(odd);<br /> DefaultMutableTreeNode even = new DefaultMutableTreeNode("even");<br /> node.add(even);<br /> for (int i = 0; i < 50; i++) {<br /> if (i % 2 == 0) {<br /> even.add(new DefaultMutableTreeNode(i));<br /> } else {<br /> odd.add(new DefaultMutableTreeNode(i));<br /> }<br /> }<br /> // The above changes may not seem to take effect until nodeStructureChanged is called<br /> model.nodeStructureChanged(node);<br />}<br />...</font><br /><br />This example creates 52 new nodes. If they were inserted with insertNodeInto() then 52 TreeNodesInserted events would have been fired. This way only one fireTreeStructureChanged event is fired after all the nodes are modified. Depending upon the particular needs of your application this may save some event processing.<br /><br /><b>Modifying user objects in a DefaultMutableTreeNodes.</b><br /><br />Just as a DefaultTreeModel needs to be notified when children are added or removed for a node, so too does it need to be notified when the user object returned by getUserObject() is modified. Failure to do so could lead to rendering issues in the JTree. This is an example of what a such a situation might look like.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsM434mZ50QKWuABjl_P0gkxFfS6jYggiKrrjit77agrEatWhNFEvcYDGGduuYtfSiOJ3lzdSyitTKQpa6v4heYQNcnjRLldMrKWTrqjSNUPLbSGfc9TLLkWX2d41DdruzU4h2iZLOoZ0/s1600-h/TreeModelDemo2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsM434mZ50QKWuABjl_P0gkxFfS6jYggiKrrjit77agrEatWhNFEvcYDGGduuYtfSiOJ3lzdSyitTKQpa6v4heYQNcnjRLldMrKWTrqjSNUPLbSGfc9TLLkWX2d41DdruzU4h2iZLOoZ0/s320/TreeModelDemo2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5180012692681295906" /></a><br /><br />To avoid this make sure to call nodeChanged() on the model after any change that might effect the rendering of that node. Here is an example of code that will update correctly. <br /><br /><font face="Courier New">...<br />TreePath currentSelection = tree.getSelectionPath();<br />if (currentSelection != null) {<br /> DefaultMutableTreeNode node = (DefaultMutableTreeNode)<br /> currentSelection.getLastPathComponent();<br /><br /> node.setUserObject("THIS IS A VERY LOOOOOOOOOOOOOOOOOOOOONG STRING");<br /><br /> DefaultTreeModel model = ((DefaultTreeModel) tree.getModel());<br /> model.nodeChanged(node);<br />}<br />...</font><br /><br />When updated correctly the tree will display the entire string. <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif306RiltyBIzMSJiBQW9y_4HAbsRL700lADGvsE-I1Lj0wTKxN0OoBi-UxzVgjp1sN6C4_m1vguK2zghNDJWOWyNKuvkSrdsKqVo7Mx1i6U9yxsrYtG6jOmpg0_RRAeXC4A6VJkYdsrE/s1600-h/TreeModelDemo3.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif306RiltyBIzMSJiBQW9y_4HAbsRL700lADGvsE-I1Lj0wTKxN0OoBi-UxzVgjp1sN6C4_m1vguK2zghNDJWOWyNKuvkSrdsKqVo7Mx1i6U9yxsrYtG6jOmpg0_RRAeXC4A6VJkYdsrE/s320/TreeModelDemo3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5180015286841542706" /></a><br /><br /><b>Conclusion</b><br />As a general rule any time the data associated with your DefaulTreeModel changes it is important to update the model. Proper use of the DefaulTreeModel will limit the number of times you must explicitly do this. <br /><br />I've made the source code for the <a href="http://docs.google.com/Doc?id=dfdmcs8w_16ftth9nfs"> example available for download</a>.<br /><br />As I said this is a draft of a tutorial. I'm looking for feedback on what can be improved and added while staying within the scope of the DefaulTreeModel/DefaultMutableTreeNode.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-37217497761114321232008-03-10T17:30:00.000-07:002008-03-10T19:06:09.547-07:00Try and Try AgainWhile working on the code in my last post it occurred to me that reusable exception handling was decent idea. It might even be nice to create a version of try that takes a parameter that handed specific types of exceptions.<br /><br />Example:<br /><pre>try {<br /> FileReader fr = new FileReader("Readme.txt");<br /> fr.read();<br />} catch (FileNotFoundException e) {<br /> e.printStackTrace();<br />} catch (IOException e) {<br /> e.printStackTrace();<br />}</pre>A enhanced version might look like this.<br /><pre>try(new LogAndContinueExceptionHandler()){<br /> FileReader fr = new FileReader("Readme.txt");<br /> fr.read();<br />}</pre>LogAndContinueExceptionHandler looks like this.<br /><pre>private static interface ExceptionHandler {<br /> public void uncaughtException(Runnable source, Throwable e);<br />}<br />public class LogAndContinueExceptionHandler implements ExceptionHandler{<br /> @Override<br /> public void uncaughtException(Runnable source, final Throwable e) {<br /> Logger.getLogger("ErrorLogger").log(Level.SEVERE, e.getMessage(), e);<br /> }<br />}</pre>If all you are going to do is log the exception, then maybe a default implementation in Logger makes sense. <pre>try(Logger.logALL("MyLogName")){<br /> FileReader fr = new FileReader("Readme.txt");<br /> fr.read();<br />}</pre>And for all of you who think checked exception are the bane of all existence there is this option.<pre>public class ToRuntime implements ExceptionHandler{<br /> @Override<br /> public void uncaughtException(Runnable source, final Throwable e) {<br /> throw new RuntimeException(e);<br /> }<br />}<br /></pre>This rethrows any exception it gets a hold of as a runtime exception. This would drive me nuts, but some people really do hate checked exceptions.<br /><br />It would be nice to be able to supply different logic for different types of exceptions, just like you do in a try/catch block. Also if one could handle some logic in the reusable section and override that by implementing a catch block you could get more flexibility. <br /><br />Example:<br /><pre><br />try {<br /> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());<br />} catch (ClassNotFoundException e1) {<br /> e1.printStackTrace();<br />} catch (InstantiationException e1) {<br /> e1.printStackTrace();<br />} catch (IllegalAccessException e1) {<br /> e1.printStackTrace();<br />} catch (UnsupportedLookAndFeelException e1) {<br /> e1.printStackTrace();<br />}<br /><br />try(new ReflectionHandeler()){<br /> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());<br />} catch (UnsupportedLookAndFeelException e1) {<br /> e1.printStackTrace();<br />}<br /><br />public class ReflectionHandeler implements ExceptionHandler{<br /> public void uncaughtException(Runnable source, final ClassNotFoundException e) {<br /> // do something important<br /> }<br /><br /> public void uncaughtException(Runnable source, final InstantiationException e) {<br /> // do something important<br /> }<br /><br /> public void uncaughtException(Runnable source, final IllegalAccessException e) {<br /> // do something important<br /> } <br />}<br /></pre><br />ReflectionHandeler might be useful someplace else... Ok this wasn't the best example, but you get the idea. Some get caught in the handler and some get caught in the catch.<br /><br />Where a pattern for handling an exception or types of exceptions is repeatable building a reusable part can help unclutter and your code and make it more maintainable. Now if the task of handling a particular type of exception changes code need only be changed in one place, not scattered throughout the code in try blocks. <br /><br />It's an idea. I may try to hack the javac compiler to support something like this... not that I'm sure even where to begin.Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-6790609626295816719.post-20751615740627504692008-03-09T07:28:00.000-07:002008-03-09T19:59:32.224-07:00More First Class Methods CodeI'm still experimenting with the prototype implementation of the <a href="http://www.jroller.com/scolebourne/entry/fcm_prototype_available">FCM closures proposal</a>. Here are a few utility methods that immediately came to mind.<br /><br /><span style="font-weight: bold;">withLock </span>-- This one I've seen an example of online a few times. It automatically release the lock when the block of code finishes executing.<br /><pre>public static void withLock(Lock lock, Runnable task) {<br /> lock.lock();<br /> try {<br /> task.run();<br /> } catch (Exception e) {<br /> throw e;<br /> } finally {<br /> lock.unlock();<br /> }<br />}<br /></pre><span style="font-style: italic;">Example Usage:</span><br /><pre>final ReentrantLock lock = new ReentrantLock();<br /> withLock(lock, #{<br /> // call some important locked code<br />});<br /></pre>Now I'm not too fond of unchecked Exceptions, but rethrowing is still better then swallowing every exception that escapes the Runnable. Here is my second attempt.<br /><pre>private static interface ExceptionHandler {<br /> public void uncaughtException(Runnable source, Throwable e);<br />}<br /><br />public static void withLock(Lock lock, Runnable task, ExceptionHandler eHandel) {<br /> lock.lock();<br /> try {<br /> task.run();<br /> } catch (Exception e) {<br /> eHandel.uncaughtException(task, e);<br /> } finally {<br /> lock.unlock();<br /> }<br />}</pre><span style="font-style: italic;">Example Usage:</span><pre>withLock(lock, #{<br /> // do some code that needs to be locked.<br /> // throw an exception to test the event handler.<br /> throw new RuntimeException("year is even error");<br /> }, #(Runnable source, Throwable e){<br /> // inline implemented event handler.<br /> e.printStackTrace();<br /> }<br />);<br /></pre>Once side effect of this is now one might be able to write reusable exception handling code. There have been plenty of times where a method I'm executing throws an exception, but there is no real way for me to recover. All I can do is log the failure and continue on. I end up writing the same try catch block over and over. It would be nice to write something like this once and reuse it. <pre>public class LogAndContinueExceptionHandler implements ExceptionHandler{<br /> public void uncaughtException(Runnable source, final Throwable e) {<br /> SwingUtilities.invokeLater(#{<br /> Logger.getLogger("ErrorLogger").log(Level.SEVERE, e.getMessage(), e);<br /> });<br /> }<br />}</pre><span style="font-style: italic;">Example Usage:</span><pre>withLock(lock, #{<br /> // do some code that needs to be locked.<br /> // throw an exception to test the event handler.<br /> throw new RuntimeException("year is even error");<br /><br /> }, new LogAndContinueExceptionHandler());<br /></pre>I used this idea when exploring another area that might benefit from closures. The EDT thread scheduling methods.<br /><br /><span style="font-weight: bold;">invokeAndWait</span><br /><pre>public static void invokeAndWait(Runnable r, ExceptionHandler eHandel){<br /> try {<br /> SwingUtilities.invokeAndWait(r);<br /> catch (InterruptedException e) {<br /> eHandel.uncaughtException(e);<br /> } catch (InvocationTargetException e) {<br /> eHandel.uncaughtException(r, e.getCause());<br /> }<br />}<br /><br />public class AlertAndContinueExceptionHandler implements ExceptionHandler{<br /> public void uncaughtException(Runnable source, final Throwable e) {<br /> Runnable showMessageBox = #{<br /> JOptionPane.showMessageDialog(null, e.getMessage());<br /> };<br /> if(SwingUtilities.isEventDispatchThread()){<br /> showMessageBox.run();<br /> }else{<br /> SwingUtilities.invokeLater(showMessageBox);<br /> }<br /> }<br />}</pre><span style="font-style: italic;">Example Usage:</span><pre>invokeAndWait(#{<br /> JOptionPane.showMessageDialog(null, "Hello World");<br />}, new AlertAndContinueExceptionHandler());</pre><span style="font-weight: bold;">simpleSwingWorker</span> -- The simpleSwingWorker method is an attempt to apply closures to the simple case of, "do something off the EDT then do something ON the EDT".<pre>public static void simpleWorker(final Runnable offEDT, final Runnable onEDT){<br /> Thread bGround = new Thread(#{<br /> offEDT.run();<br /> SwingUtilities.invokeLater(onEDT);<br />});<br />bGround.start();<br />}<br /></pre><span style="font-style: italic;">Example Usage:</span><pre>simpleWorker(#{<br /> System.out.println("sleep");<br /> Thread.currentThread().sleep(1000);<br /> System.out.println("start");<br /> }, #{<br /> JOptionPane.showMessageDialog(null, "Hello World");<br />});</pre>I also wrote a blocking version based on invokeAndWait. <pre>public static void simpleBlockingWorker(final Runnable offEDT, final Runnable onEDT){<br /> Thread bGround = new Thread(#{<br /> offEDT.run();<br /> SwingUtilities.invokeAndWait(onEDT);<br /> });<br /> bGround.start();<br />}</pre><br />Hay wait a second, invokeAndWait throws exceptions right? And yet the code as it stands compiles without issue. I think I may have found a bug in the FCM prototype. <br /><br />Some may look at these examples and point out that this type of functionality may really fall into the realm of "control structures". There is some debate in the Java community as to weather the common man should be allowed to write their own control structures. Some prefer to leave this responsibility with the language designers. It is my opinion that once you make this type of code easy, then you already have allowed the common man to make their own control structures. In that case you might as well legitimize the pattern and provide as much support as it possible. <br /><br />I look forward to the day when there is a Java Control Abstraction (JCA) prototype to play around with.Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-6790609626295816719.post-56807197755023137582008-03-05T20:22:00.000-08:002008-03-07T14:37:17.645-08:00Experimenting with FCMStephen Colebourne has a prototype implementation of the <a href="http://www.jroller.com/scolebourne/entry/fcm_prototype_available">FCM closures proposal</a>. I took some time to download and play with the new syntax and functionality.<br /><br /><span style="font-weight: bold;"></span>I tried to focus on some of the functionality FCM provides that other closure proposals do not. Namely method and field literals. I think FCM can help create flexible models for swing components. In the past I've done this using plain reflection but FCM makes things easier.<br /><br />This is a screenshot of the demo application.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigd6rAXPPFa3zkyRGH1xhyphenhyphenENtCYhMwlJGRQryFm6JUZty_1SmlGmGzwn2Iz0sHO1m1LVb5rzbpHk50ebG2OQbena3sOaS97LFqhR38grQ_ISXwfY2OpDQRTn3L3kslqQdDp8Zk5Ex3Gfc/s1600-h/fcmDemo.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigd6rAXPPFa3zkyRGH1xhyphenhyphenENtCYhMwlJGRQryFm6JUZty_1SmlGmGzwn2Iz0sHO1m1LVb5rzbpHk50ebG2OQbena3sOaS97LFqhR38grQ_ISXwfY2OpDQRTn3L3kslqQdDp8Zk5Ex3Gfc/s320/fcmDemo.png" alt="" id="BLOGGER_PHOTO_ID_5175091465833728066" border="0" /></a>It displays three lists and one table all of which are backed by the same collection of SuperHero beans.<br /><br />I found that I could apply FCM in one of the first few lines of my demo.<br /><pre>SwingUtilities.invokeLater(#startApp());</pre>This calls my startApp() method on the event dispatch thread. I have to admit that while I'm not a fan of in-lining this was preferable to the traditional method of building a Runnable and passing that to invokeLater.<br /><br />Here is a very basic ListModel that can expose any public field of an object.<br /><pre> /**<br />* Wraps a list of elements and exposes one field to the list model<br />* interface<br />*<br />* @param <E><br />*/<br />private class FCMFieldListModel<T> extends AbstractListModel {<br /><br />private List<T> backingList;<br />private Field targetField;<br /><br />public FCMFieldListModel(List<T> data, Field target) {<br /> backingList = data;<br /> targetField = target;<br />}<br /><br />@Override<br />public Object getElementAt(int index) {<br /> try {<br /> return targetField.get(backingList.get(index));<br /> } catch (IllegalArgumentException e) {<br /> e.printStackTrace();<br /> } catch (IllegalAccessException e) {<br /> e.printStackTrace();<br /> }<br /> return null;<br />}<br /><br />@Override<br />public int getSize() {<br /> return backingList.size();<br />}<br /><br />}<br /></pre>In my case I'm building a list of superheroes and wish to expose the last name field.<br /><pre><br />FCMFieldListModel<SuperHero> lastListModel = new FCMFieldListModel<SuperHero>(heroes, SuperHero#lastName);<br /></pre><br />The last parameter is the new field literal syntax from FCM. It allows me a type safe way of specifying which field I want to expose.<br /><br />To create more list models that expose other fields I just need to create another FCMFieldListModel and expose a different field.<br /><br /><pre>FCMFieldListModel<SuperHero> listModel = new FCMFieldListModel<SuperHero>(heroes, SuperHero#firstName);<br /></pre><br />Now most of the time a bean will not expose a field. Instead getter/setter methods are used to expose this information. FCM has method literals as well as field literals. The code for a FCMMethodListModel is very much like FCMFieldListModel but it takes a Method reference instead of a field refrence.<br /><pre><br /> /**<br /> * Wraps a list of elements and exposes one method to the list model<br /> * interface<br /> *<br /> * @param <E><br /> */<br />private class FCMMethodListModel<E> extends AbstractListModel {<br /><br /> private List<E> backingList;<br /> private Method targetMethod ;<br /><br /> public FCMMethodListModel(List<E> data, Method target) {<br /> backingList = data;<br /> targetMethod = target;<br /> }<br /><br /> @Override<br /> public Object getElementAt(int index) {<br /> try {<br /> Object item = backingList.get(index);<br /> return targetMethod.invoke(item);<br /> } catch (IllegalArgumentException e) {<br /> e.printStackTrace();<br /> } catch (IllegalAccessException e) {<br /> e.printStackTrace();<br /> } catch (InvocationTargetException e) {<br /> e.printStackTrace();<br /> }<br /> return null;<br /> }<br /><br /> @Override<br /> public int getSize() {<br /> return backingList.size();<br /> }<br /><br />}<br /></pre>Basically this lets you tell the list model which method of your bean to call to represent your bean in the list.<br /><br />Finally I used this same technique to build a table model.<br /><pre>FCMTableModel<SuperHero> superTableModel = new FCMTableModel<SuperHero>(heroes,<br /> SuperHero#getFirstName(), SuperHero#getLastName(),<br /> SuperHero#getHeroName(), SuperHero#getFirstAppearance());</pre><br />The constructor takes a list of SuperHeroes and a variable list of methods to use as columns. This code is all it takes to populate the table seen in the center of the screenshot. <span style="font-style: italic;"></span><br /><br />The first appearance column of the super hero table is a date without a day. I decided to use an anonymous inner method to implement a cellrendrer to format the date.<br /><pre>TableColumn dateColumn = superHeroTable.getColumn(SuperHero#getFirstAppearance().getName());<br /><br />// in-line cell renderer for the date column of the superhero table<br />dateColumn.setCellRenderer(#(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column){<br />if(isSelected){<br /> dateRenderer.setBackground(UIManager.getColor("Table.selectionBackground"));<br />}else{<br /> dateRenderer.setBackground(UIManager.getColor("Table.background"));<br />}<br />dateRenderer.setText(sdfDateColumn.format((Date) value));<br />return dateRenderer;<br />});</pre>Overall I have to say I like FCM. Even in it's incomplete state I feel it's still a step forward. The method and field literals were very convenient and useful. I can see myself creating generalized adapter classes around this functionality. As I said before I'm not a fan of in-lining code. I tend not to in-line inner classes either. But the anonymous inner method syntax seemed straight forward enough. Also I much prefer using the # syntax to pass method to invokeLater.<br /><br />My examples involved a bean and it's associated methods. Yet there is no way currently to specify that a method used as a parameter come from any specific class. It certainly would be an error to pass a FCMMethodListModel<superhero> System.out#println(Object). I think this is one of the items on the list of things to do for FCM but I'm not certain.<br /><br />I've made the <a href="http://docs.google.com/Doc?id=dfdmcs8w_13c79fpvg3">source code for this demo</a> available on google docs. Just remember that you need the <a href="http://www.jroller.com/scolebourne/entry/fcm_prototype_available">prototype FCM implementation</a> to run it.<br /><br />Finally I'd just like to thank </superhero>Stephen for all his hard work in creating this prototype. It's been fun to experiment with it.<br /><superhero><br /><span style="font-style: italic;">Note:</span><br /><span style="font-style: italic;">This is demo level code. It was written to play around with FCM and a such the models are not as robust as they could be. Their job is to adapt an unchanging list of beans using FCMs method literals. I will probably revisit FCMTableModel at some point to show how it could be enhanced.</span> <span style="font-style: italic;">As it stands now they have no support for events. Also I'm not sure a cell renderer was a good choice for an anonymous inner method example. If you read the javadoc for </span><a style="font-style: italic;" href="http://java.sun.com/javase/6/docs/api/javax/swing/table/DefaultTableCellRenderer.html">DefaultTableCellRenderer</a><span style="font-style: italic;"> it talks about overriding some of the JLabel methods for performance reasons. </span><br /><br /></superhero>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-9051260184246770292008-01-14T17:02:00.000-08:002008-01-14T19:29:26.029-08:00Named Paramaters and Layout ManagersI had made a hasty comment on a java.net post that named parameters made a lot of scene for layout managers. Of course it started to bother me because while I felt I was right, I really had only ever used <a href="http://dbcsoftware.com/">one language that had named parameters</a>. That language had a lot of weird things in it and so I had to do some experimentation to see if I might actually be right. To that end I tried to implement something that looked syntactically like named parameters for GridBagLayout.<br /><br />If Java supported a variable list of named parameters for layouts it might look like this:<br /><br /><span style="font-family:monospace;"><span style="font-size:85%;">add(new JLabel("test"), gridx:1, gridy:1, weight:1, fill:BOTH);</span></span><br /><br />Now there is a couple ways to actually write this today.<br /><br />Example : 1<br /><br /><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;">GridBagConstraints constraints = </span></span></span><span style="color: rgb(127, 0, 85);"><span style="font-family:Monospace;"><span style="font-size:85%;"><b>new</b></span></span></span><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> GridBagConstraints();</span></span></span><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"><br />constraints.</span><span style="color: rgb(0, 0, 192);">gridx</span><span style="color: rgb(0, 0, 0);"> = 1;</span></span></span><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"><br />constraints.</span><span style="color: rgb(0, 0, 192);">gridy</span><span style="color: rgb(0, 0, 0);"> = 1;</span></span></span> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);">constraints.</span><span style="color: rgb(0, 0, 192);">fill</span><span style="color: rgb(0, 0, 0);"> = GridBagConstraints.</span><span style="color: rgb(0, 0, 192);"><i>BOTH</i></span><span style="color: rgb(0, 0, 0);">;<br />constraints.</span><span style="color: rgb(0, 0, 192);">weightx</span><span style="color: rgb(0, 0, 0);"> = 1;</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;">add(</span></span></span><span style="color: rgb(127, 0, 85);"><span style="font-family:Monospace;"><span style="font-size:85%;"><b>new</b></span></span></span><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> JLabel(</span></span></span><span style="color: rgb(0, 0, 0);"><span style="font-family:monospace;"><span style="font-size:85%;">"test"</span></span></span><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;">), constraints);</span></span></span><br /><br />This isn't so bad, but it's a little verbose, it really adds up when you have oh say a dozen or more controls in a container The next example is something I've see a certain commercial IDE create. This is just maddening to try to read.<br /><br />Example 2:</p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);">add(</span><span style="color: rgb(127, 0, 85);"><b>new</b></span><span style="color: rgb(0, 0, 0);"> JLabel(</span><span style="color: rgb(0, 0, 0);"><span style="font-family:monospace;">"test"</span></span><span style="color: rgb(0, 0, 0);">), </span><span style="color: rgb(127, 0, 85);"><b>new</b></span><span style="color: rgb(0, 0, 0);"> GridBagConstraints(1,1,1,1,1,0,GridBagConstraints.</span><span style="color: rgb(0, 0, 192);"><i>CENTER</i></span><span style="color: rgb(0, 0, 0);">, GridBagConstraints.</span><span style="color: rgb(0, 0, 192);"><i>BOTH</i></span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(127, 0, 85);"><b>new</b></span><span style="color: rgb(0, 0, 0);"> Insets(0,0,0,0), 0, 0));</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Times,serif;"><span style="font-size:100%;"><span style="color: rgb(0, 0, 0);">To see if anything would actually be saved by named parameters I built GridBagFactory. It and it's related interfaces and helpers make the following syntax possible. </span></span></span> </p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);">... create buttons in JPanel ...</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(127, 0, 85);"><b>public</b></span><span style="color: rgb(0, 0, 0);"> JavaTutorialDemo() {</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> GridBagFactory gbf = </span><span style="color: rgb(127, 0, 85);"><b>new</b></span><span style="color: rgb(0, 0, 0);"> GridBagFactory(</span><span style="color: rgb(127, 0, 85);"><b>this</b></span><span style="color: rgb(0, 0, 0);">);<br /> gbf.addDefaultConstraints(FILL.</span><span style="color: rgb(0, 0, 192);"><i>HORIZONTAL</i></span><span style="color: rgb(0, 0, 0);">);<br /> gbf.add(</span><span style="color: rgb(0, 0, 192);">button1</span><span style="color: rgb(0, 0, 0);">);<br /> gbf.add(</span><span style="color: rgb(0, 0, 192);">button2</span><span style="color: rgb(0, 0, 0);">);<br /> gbf.add(</span><span style="color: rgb(0, 0, 192);">button3</span><span style="color: rgb(0, 0, 0);">, GRID.WIDTH.</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">);<br /> gbf.add(</span><span style="color: rgb(0, 0, 192);">button4</span><span style="color: rgb(0, 0, 0);">, PAD.</span><span style="color: rgb(0, 0, 0);"><i>y</i></span><span style="color: rgb(0, 0, 0);">(40), GRID.WIDTH.</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">);<br /> gbf.add(</span><span style="color: rgb(0, 0, 192);">button5</span><span style="color: rgb(0, 0, 0);">, INSETS.</span><span style="color: rgb(0, 0, 0);"><i>top</i></span><span style="color: rgb(0, 0, 0);">(10), GRID.</span><span style="color: rgb(0, 0, 0);"><i>xy</i></span><span style="color: rgb(0, 0, 0);">(1,2), GRID.WIDTH.</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">, WEIGHT.</span><span style="color: rgb(0, 0, 0);"><i>y</i></span><span style="color: rgb(0, 0, 0);">(1), ANCHOR.</span><span style="color: rgb(0, 0, 192);"><i>PAGE_END</i></span><span style="color: rgb(0, 0, 0);"> );</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> }</span></span></span></p> <p style="margin-bottom: 0in;" align="left"></p><p style="margin-bottom: 0in;" align="left"><span style="font-family:Times,serif;"><span style="font-size:100%;">You can compare this to the <a href="http://java.sun.com/docs/books/tutorial/uiswing/layout/gridbag.html">original code</a> in the Java tutorial . The relevant GridBag code weighs in at about 35 lines of code. GridBagFactory can do it in 6. Now code density is meaningless if it's not immediately clear what the line of code is doing. There is some noise with the declaration of the GridBagFactory object, but with a little inheritance and overriding I created GBFPanel. A JPanel integrated with a GridBagFactory. </span></span> </p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(127, 0, 85);">... create buttons in JPanel ...<br /><b>public</b></span><span style="color: rgb(0, 0, 0);"> JavaTutorialDemo() {</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> addDefaultConstraints(FILL.</span><span style="color: rgb(0, 0, 192);"><i>HORIZONTAL</i></span><span style="color: rgb(0, 0, 0);">);</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> add(</span><span style="color: rgb(0, 0, 192);">button1</span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button2</span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button3</span><span style="color: rgb(0, 0, 0);">, GRID.WIDTH.</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button4</span><span style="color: rgb(0, 0, 0);">, PAD.</span><span style="color: rgb(0, 0, 0);"><i>y</i></span><span style="color: rgb(0, 0, 0);">(40), GRID.WIDTH.</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button5</span><span style="color: rgb(0, 0, 0);">, INSETS.</span><span style="color: rgb(0, 0, 0);"><i>top</i></span><span style="color: rgb(0, 0, 0);">(10), GRID.</span><span style="color: rgb(0, 0, 0);"><i>xy</i></span><span style="color: rgb(0, 0, 0);">(1,2), GRID.WIDTH.</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">, WEIGHT.</span><span style="color: rgb(0, 0, 0);"><i>y</i></span><span style="color: rgb(0, 0, 0);">(1), ANCHOR.</span><span style="color: rgb(0, 0, 192);"><i>PAGE_END</i></span><span style="color: rgb(0, 0, 0);"> );</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> }</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Times,serif;"><span style="font-size:100%;">Thats darn close to the named parameter syntax from the beginning. If we rewrite it in that style it becomes just a tad clearer. </span></span> </p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(127, 0, 85);">... create buttons in JPanel ...<br /><b>public</b></span><span style="color: rgb(0, 0, 0);"> JavaTutorialDemo() {</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> addDefaultConstraints(FILL.</span><span style="color: rgb(0, 0, 192);"><i>HORIZONTAL</i></span><span style="color: rgb(0, 0, 0);">);</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="color: rgb(0, 0, 0);"><span style="font-family:Monospace;"><span style="font-size:85%;"> </span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);"> add(</span><span style="color: rgb(0, 0, 192);">button1</span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button2</span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button3</span><span style="color: rgb(0, 0, 0);">, gridwidth:</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button4</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);"><i>pady:</i></span><span style="color: rgb(0, 0, 0);">40, gridwidth:</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">);<br /> add(</span><span style="color: rgb(0, 0, 192);">button5</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);"><i>top:</i></span><span style="color: rgb(0, 0, 0);">10, gridx:1, gridy:2, gridwidth:</span><span style="color: rgb(0, 0, 192);"><i>REMAINDER</i></span><span style="color: rgb(0, 0, 0);">, weighty:1, anchor:</span><span style="color: rgb(0, 0, 192);"><i>PAGE_END</i></span><span style="color: rgb(0, 0, 0);"> );<br /></span></span></span></p><p style="margin-bottom: 0in;" align="left"><span style="font-family:Monospace;"><span style="font-size:85%;"><span style="color: rgb(0, 0, 0);">}</span></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Times,serif;"><span style="font-size:100%;">I tried to recreate a number of layout examples from other very powerful layouts, like MigLayout and PageLayout. In my test cases I came in really close in code density to those other layout managers. My point really is not about any particular layout manager but about named parameters. In this case it seems to increase readability and code density. So I feel better about my hasty statement, I think all layout managers would benefit from named <span style="font-family:Times,serif;"><span style="font-size:100%;"><span style="font-family:Times,serif;"><span style="font-size:100%;"><span style="font-family:Times,serif;">parameters.</span></span></span></span></span> <style type="text/css"> <!-- @page { size: 8.5in 11in; margin: 0.79in } P { margin-bottom: 0.08in } --> </style> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Times,serif;"><span style="font-size:100%;"><span style="font-family:Times,serif;"></span></span></span> </p></span></span></p> <p style="margin-bottom: 0in;" align="left"><span style="font-family:Times,serif;"><span style="font-size:100%;">I'll post the full code for GridBagFactory just as soon as I find a place to host it. </span></span> </p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-20907180208256684142007-08-02T06:23:00.000-07:002007-08-04T12:29:30.718-07:00API changesThe icon demo was a great experience, but, it did bring up some API issues.<br /><br />First the getScaledInstance() call in the original code. I getScaledInstance() because it's one line and rather self documenting. It didn't get in the way by adding any complexity. The alternative would be to use a BufferedImage and a Graphics object. The best quality approach from Chris Campbell's article was a lot more code then I wanted to add. This demo is about Icons after all not Java2D. In the end I compromised on about 8 lines of code to do a simple bilinear downscaling. I think a more robust version of this faster method of resizing should be packaged up in a nice neat single call.<br /><br />Something like this ..<br /><br />public static BufferedImage BufferedImage.getScaledImage(BufferedImage src, width, height,<br />Object hint, boolean multipass);<br /><br />I'm not sure if BufferedImage is the correct place for it or not.<br /><br />Also while SwingWorker is very useful I really hate having to return null at the end of doInBackground(). I wish there was an "IntermediateSwingWroker" that just didn't return anything from doInBackground().<br /><br />Other then that things went smoothly. I'm glad my changes had zero impact on Java Web Start.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-66859615687237686592007-08-01T17:00:00.000-07:002007-08-02T08:26:05.604-07:00I got published!My <a href="http://aberrantcode.blogspot.com/2007/07/reworking-reworking-of-icon-demo.html">first post</a> on this blog was complaining about the current state of the <a href="http://blogs.sun.com/thejavatutorials/entry/reworking_the_icondemo">icon demo app</a> from the Java tutorial page. I even wrote some replacement code. It received a lot more attention then I ever thought was possible. I got some great comments, Mikael Grev (of <a href="http://miglayout.com/">MiG Layout fame</a>), blogged about it on <a href="http://www.javalobby.org/java/forums/t99065.html">javalobby</a> and finally I was contacted by Sharon Zakhour of Sun. She asked if they could include my code in the tutorial. I was very happy to say yes. I revised my code a little more, helped rewrite some of the page and <a href="http://blogs.sun.com/thejavatutorials/entry/icondemo_collaboration#comments">submitted my code and documentation to Sharon</a>. It went through an engineering review. There was some discussion about my usage of Image.getScaledInstance() and a tweak was added to use the resizing strategy put forth in <a href="http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">The Perils of Image.getScaledInstance()</a>. All in all it was an enjoyable experience. <a href="http://blogs.sun.com/thejavatutorials/entry/java_se_tutorials_update1#comments">And now my code has actually been pushed to the web</a>. It's now officially part of the <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/icon.html">How to Use Icons</a> section of the Java tutorial.<br /><br />Would I do it again? Yes, absolutely. Everyone at sun was great.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6790609626295816719.post-82993884805771690292007-07-23T04:59:00.000-07:002007-08-02T03:59:04.121-07:00Collections deepEquals()There was some discussion on <a href="http://weblogs.java.net/blog/emcmanus/">Eamonn McManus's blog</a> (java.net) about Arrays.deepEquals() and a collections version.<span style="text-decoration: underline;"> </span><a href="http://weblogs.java.net/blog/emcmanus/archive/2007/07/comparing_objec.html">http://weblogs.java.net/blog/emcmanus/archive/2007/07/comparing_objec.htm</a>l<br /><br />I've decided to construct a first attempt at this. The idea is that two lists are deepEqual if the elements in them are equal. Of course the elements in a list can be other lists and so on. This applies to Sets and Maps also. This code attempts to be generic enough to compare two collections of the same type for deep equality. This is only a first attempt I'm sure there can be some enhancements/optimizations made.<br /><br /><br />NOTE: It does not (yet) handle collections that include themselves.<br /><br /><pre><br />import java.util.ArrayList;<br />import java.util.Collection;<br />import java.util.HashMap;<br />import java.util.List;<br />import java.util.Map;<br />import java.util.Set;<br />import java.util.TreeSet;<br /><br /><br />public class DeepEqualsTest {<br /><br /> /**<br /> * @param args<br /> */<br /> public static void main(String args[]) {<br /><br /> ArrayList<Object> stuff1 = new ArrayList<Object>();<br /> stuff1.add(null);<br /> stuff1.add("A");<br /> stuff1.add(new Object[] { "B", "C", null });<br /><br /> ArrayList<Object> stuff2 = new ArrayList<Object>();<br /> stuff2.add(null);<br /> stuff2.add("A");<br /> stuff2.add(new Object[] { "B", "C", null });<br /><br /> TreeSet<Integer> ts1 = new TreeSet<Integer>();<br /> ts1.add(1);<br /> ts1.add(3);<br /> ts1.add(2);<br /><br /> TreeSet<Integer> ts2 = new TreeSet<Integer>();<br /> ts2.add(1);<br /> ts2.add(2);<br /> ts2.add(3);<br /> // ts2.add(4);<br /> <br /> HashMap<String, Object> map1 = new HashMap<String, Object>();<br /> HashMap<String, Object> map2 = new HashMap<String, Object>();<br /><br /> map1.put("key1", new Object[] { ts1, "hi", 123, 456 });<br /> map2.put("key1", new Object[] { ts2, "hi", 123, 456 });<br /><br /> stuff1.add(map1);<br /> stuff2.add(map2);<br /><br /> System.out.println("Result = " + deepEquals(stuff1, stuff2));<br /> }<br /><br /> /**<br /> * An implementation of "deep equals" for collection classes, built in the<br /> * Arrays.deepEquals() style. It attempts to compare equality based on the<br /> * contents of the collection.<br /> *<br /> * @param t1 -<br /> * first object, most likely a collection of some sort.<br /> * @param t2 -<br /> * second object, most likely a collection of some sort.<br /> * @return - true if the content of the collections are equal.<br /> */<br /> public static <T> boolean deepEquals(T t1, T t2) {<br /><br /> if (t1 == t2) {<br /> return true;<br /> }<br /><br /> if (t1 == null || t2 == null) {<br /> return false;<br /> }<br /><br /> if (t1 instanceof Map && t2 instanceof Map) {<br /> return mapDeepEquals((Map<?, ?>) t1, (Map<?, ?>) t2);<br /> } else if (t1 instanceof List && t2 instanceof List) {<br /> return linearDeepEquals((List<?>) t1, (List<?>) t2);<br /><br /> } else if (t1 instanceof Set && t2 instanceof Set) {<br /> return linearDeepEquals((Set<?>) t1, (Set<?>) t2);<br /><br /> } else if (t1 instanceof Object[] && t2 instanceof Object[]) {<br /> return linearDeepEquals((Object[]) t1, (Object[]) t2);<br /><br /> } else {<br /> return t1.equals(t2);<br /> }<br /> }<br /><br /> /**<br /> * Compares two maps for equality. This is based around the idea that if the<br /> * keys are deep equal and the values the keys return are deep equal then<br /> * the maps are equal.<br /> *<br /> * @param m1 -<br /> * first map<br /> * @param m2 -<br /> * second map<br /> * @return - weather the maps are deep equal<br /> */<br /> private static boolean mapDeepEquals(Map<?, ?> m1, Map<?, ?> m2) {<br /> if (m1.size() != m1.size()) {<br /> return false;<br /> }<br /><br /> Set<?> allKeys = m1.keySet();<br /> if (!linearDeepEquals(allKeys, m2.keySet())) {<br /> return false;<br /> }<br /><br /> for (Object key : allKeys) {<br /> if (!deepEquals(m1.get(key), m2.get(key))) {<br /> return false;<br /> }<br /> }<br /> return true;<br /> }<br /><br /> /**<br /> * Compares two Collections for deep equality.<br /> *<br /> * @param s1<br /> * @param s2<br /> * @return<br /> */<br /> private static boolean linearDeepEquals(Collection<?> s1, Collection<?> s2) {<br /> if (s1.size() != s2.size()) {<br /> return false;<br /> }<br /><br /> for (Object s1Item : s1) {<br /> boolean found = false;<br /> for (Object s2Item : s2) {<br /> if (deepEquals(s2Item, s1Item)) {<br /> found = true;<br /> break;<br /> }<br /> }<br /> if (!found) {<br /> return false;<br /> }<br /> }<br /> return true;<br /> }<br /><br /> /**<br /> * Compares two Object[] for deep equality<br /> *<br /> * @param s1<br /> * @param s2<br /> * @return<br /> */<br /> private static boolean linearDeepEquals(Object[] s1, Object[] s2) {<br /><br /> if (s1.length != s2.length) {<br /> return false;<br /> }<br /><br /> for (Object s1Item : s1) {<br /> boolean found = false;<br /> for (Object s2Item : s2) {<br /> if (deepEquals(s2Item, s1Item)) {<br /> found = true;<br /> break;<br /> }<br /> }<br /> if (!found) {<br /> return false;<br /> }<br /> }<br /> return true;<br /> }<br />}<br /></pre>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-6790609626295816719.post-4374801419583637542007-07-18T12:39:00.000-07:002007-07-21T05:04:06.803-07:00Reworking the reworking of the Icon demo<span style="font-size:100%;"><span style="font-family:arial;">Java.net posted an article about the rewriting of some of the demo code in the java tutorial.</span><br /><br /><a style="font-family: arial;" href="http://blogs.sun.com/thejavatutorials/entry/reworking_the_icondemo">http://blogs.sun.com/thejavatutorials/entry/reworking_the_icondemo</a><br /><br /><span style="font-family:arial;">I was not pleased with the overall quality of this "reworked" demo code. It turns out I'm not alone. Not to be one to criticize and not contribute I am posting my own revision. I'm sure someone can pick my code apart too, but I feel it's better then what sun has provided.</span><br /><br /><span style="font-family:arial;">As far as I'm concerned demo code should do three things:</span><br /></span><ol style="font-family:arial;"><li><span style="font-size:100%;">Focus on the topic</span></li><li><span style="font-size:100%;">Be informative<br /></span></li><li><span style="font-size:100%;">Exemplify best practices</span></li></ol><span style="font-size:100%;"><a style="font-family: arial;" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz-lar93LuSHlQlKdPwOHJtItbbj0JTEI7eQCP2hkXBWWlEOr00XzsqMonPjIiajUWgFLH89vkCwJzYsyiHtdVpFSFDv-RIopbDeCZk0PnCruYU_MGdhOp7EQ18Ri2WdiQyVGQDmIHwfs/s1600-h/IconDemo1.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz-lar93LuSHlQlKdPwOHJtItbbj0JTEI7eQCP2hkXBWWlEOr00XzsqMonPjIiajUWgFLH89vkCwJzYsyiHtdVpFSFDv-RIopbDeCZk0PnCruYU_MGdhOp7EQ18Ri2WdiQyVGQDmIHwfs/s320/IconDemo1.png" alt="" id="BLOGGER_PHOTO_ID_5088670648671967970" border="0" /></a><span style="font-family:arial;">To that effect I redesigned the demo a little bit. Instead of "Next" and "Previous" buttons I build a toolbar that has thumbnails of each image. This gives us the opportunity to make more Icons, which of course is the point. I had debated removing the background SwingWorker that loads the images. It just doesn't seem to be necessary to talk about that in the icon demo. In the end the "best practice" idea won out and I built a new SwingWorker that populates the buttons and creates the images and the thumbnails. Some of you more experienced Swing programmers may notice I call getScaledInstance() to create the thumbnail. I am aware that Chris Campbell has a blog entry called <a href="http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">The Perils of Image.getScaledInstance()</a>. In this demo though we are not resizing an image in a paint method that will be called lots of times, we do it one per image in a background thread. Performances wise I don't think it's an issue.<br /><br />I have to admit I'm new to the JDK6 version of SwingWorker. I don't really like the use of Void in the parameters because I have to return null at the end of doInBackground(). The thing is all the work is done in the calls to process.<br /><br />Also I've never done a webstart application so I'm not sure if I removed anything important to webstart.<br /><br />All in all I think my demo more readable and concise. With spacing and javadoc comments it weighs in around 180 lines. The original was about 350 lines of code, and had almost no comments. So here is the code in all it's glory. I would gladly accept any feedback people have on how to make it even more clear for a beginner.</span></span><br /><br /><pre><br />import java.awt.BorderLayout;<br />import java.awt.Image;<br />import java.awt.event.ActionEvent;<br />import java.awt.event.ActionListener;<br />import java.util.HashMap;<br />import java.util.LinkedHashMap;<br />import java.util.List;<br />import java.util.Map;<br /><br />import javax.swing.BorderFactory;<br />import javax.swing.Box;<br />import javax.swing.ImageIcon;<br />import javax.swing.JButton;<br />import javax.swing.JFrame;<br />import javax.swing.JLabel;<br />import javax.swing.JToolBar;<br />import javax.swing.SwingUtilities;<br />import javax.swing.SwingWorker;<br /><br />/**<br /> * Reworking of the IconDemoApp from the java tutorial. <br /> * <br /> * IconDemoApp.java requires the following files: <br><br /> * The following files are copyright 2006 spriggs.net and licenced under a<br /> * Creative Commons Licence (http://creativecommons.org/licenses/by-sa/3.0/)<br /> * <br><br /> * images/sunw01.jpg <br><br /> * images/sunw02.jpg <br><br /> * images/sunw03.jpg <br><br /> * images/sunw04.jpg <br><br /> * images/sunw05.jpg <br><br /> */<br />public class IconDemoApp extends JFrame {<br /><br /> /**<br /> * Hashmap to store the icons used in this example. The goal is to only load<br /> * them from the disk once.<br /> */<br /> private HashMap<String, ImageIcon> iconCache;<br /> private HashMap<String, ImageIcon> thumbnailCache;<br /><br /> private JLabel photographLabel;<br /> private JToolBar buttonBar = new JToolBar();<br /><br /> private String imagedir = "images/";<br /><br /> /**<br /> * List of all the descriptions of the image files. These correspond one to<br /> * one with the image file names<br /> */<br /> private String[] imageCaptions = { "Original SUNW Logo", "The Clocktower",<br /> "Clocktower from the West", "The Mansion", "Sun Auditorium" };<br /><br /> /**<br /> * List of all the image files to load.<br /> */<br /> private String[] imageFileNames = { "sunw01.jpg", "sunw02.jpg",<br /> "sunw03.jpg", "sunw04.jpg", "sunw05.jpg" };<br /><br /> /**<br /> * Main entry point to the demo. Loads the Swing elements on the "Event<br /> * Dispatch Thread".<br /> * <br /> * @param args<br /> */<br /> public static void main(String args[]) {<br /> SwingUtilities.invokeLater(new Runnable() {<br /> public void run() {<br /> IconDemoApp app = new IconDemoApp();<br /> app.setVisible(true);<br /> }<br /> });<br /> }<br /><br /> /**<br /> * Default constructor for the demo.<br /> */<br /> public IconDemoApp() {<br /> setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br /> setTitle("Icon Demo: Please Select an Image");<br /><br /> // A label for displaying the pictures<br /> photographLabel = new JLabel();<br /> photographLabel.setVerticalTextPosition(JLabel.BOTTOM);<br /> photographLabel.setHorizontalTextPosition(JLabel.CENTER);<br /> photographLabel.setHorizontalAlignment(JLabel.CENTER);<br /> photographLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));<br /><br /> // we add glue on two sides so that when we add stuff in between later<br /> // it will be centered<br /> buttonBar.add(Box.createGlue());<br /> buttonBar.add(Box.createGlue());<br /><br /> add(buttonBar, BorderLayout.SOUTH);<br /> add(photographLabel, BorderLayout.CENTER);<br /><br /> setSize(400, 300);<br /> // this centers the frame on the screen<br /> setLocationRelativeTo(null);<br /><br /> loadimages.execute();<br /> }<br /><br /> /**<br /> * SwingWorker class that loads the images a background thread and published<br /> * when a new one is ready to be displayed.<br /> * <br /> * We use Void as the first SwingWroker param as we do not need to return anything from do in background. <br /> */<br /> private SwingWorker<Void, String> loadimages = new SwingWorker<Void, String>() {<br /><br /> /**<br /> * Creates full size and thumbnail versions of the target image files.<br /> */<br /> @Override<br /> protected Void doInBackground()<br /> throws Exception {<br /><br /> iconCache = new HashMap<String, ImageIcon>();<br /> thumbnailCache = new HashMap<String, ImageIcon>();<br /><br /> for (int i = 0; i < imageCaptions.length; i++) {<br /> ImageIcon icon;<br /> icon = new ImageIcon(IconDemoApp.class<br /> .getResource((imagedir + imageFileNames[i])));<br /> iconCache.put(imageCaptions[i], icon);<br /> thumbnailCache.put(imageCaptions[i], new ImageIcon(icon<br /> .getImage().getScaledInstance(32, 32,<br /> Image.SCALE_AREA_AVERAGING)));<br /> publish(imageCaptions[i]);<br /> }<br /> // unfortunately we must return something, and only null is valid to return when the return type is void. <br /> return null;<br /> }<br /><br /> /**<br /> * Process all loaded images.<br /> */<br /> @Override<br /> protected void process(List<String> chunks) {<br /> for (String caption : chunks) {<br /> JButton thumbButton = new JButton(thumbnailCache.get(caption));<br /> thumbButton.setToolTipText(caption);<br /> thumbButton.addActionListener(new ThumbnailActionListener(<br /> caption));<br /> // add the new button BEFORE the last glue<br /> // this centers the buttons in the toolbar<br /> buttonBar.add(thumbButton, buttonBar.getComponentCount() - 1);<br /> }<br /> }<br /> };<br /><br /> /**<br /> * changes the currently displayed image<br /> * <br /> * @param caption -<br /> * the caption is also the key<br /> */<br /> private void setCurrentImage(String caption) {<br /> photographLabel.setIcon(iconCache.get(caption));<br /> photographLabel.setToolTipText(caption);<br /> setTitle("Icon Demo: " + caption);<br /> }<br /><br /> /**<br /> * Action listener that shows the image specified in it's constructor. For<br /> * use on the thumbnail buttons.<br /> */<br /> private class ThumbnailActionListener implements ActionListener {<br /> private String imageCaption;<br /><br /> public ThumbnailActionListener(String caption) {<br /> imageCaption = caption;<br /> }<br /><br /> @Override<br /> public void actionPerformed(ActionEvent e) {<br /> setCurrentImage(imageCaption);<br /> }<br /> };<br /><br />}<br /></pre>Unknownnoreply@blogger.com12