<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9018398</id><updated>2012-01-25T23:32:17.394+07:00</updated><category term='Swing'/><category term='Plugin'/><category term='Jazoon'/><category term='JSR-296'/><category term='Layout'/><category term='hooks'/><category term='Talk'/><category term='opinion'/><category term='Maven'/><category term='DesignGridLayout'/><category term='new year resolutions'/><category term='bean'/><category term='open source'/><category term='Java'/><category term='API'/><category term='Guice'/><category term='rant'/><category term='properties'/><category term='GUI'/><category term='JavaFX'/><title type='text'>Fancy one more Java cup?</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>27</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9018398.post-9008262811770358387</id><published>2009-05-01T18:21:00.032+07:00</published><updated>2009-05-04T06:03:48.679+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>DesignGridLayout: where do we stand?</title><content type='html'>It's been a long time I haven't blogged on &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;. Several events have concurred to this lack of communication:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I have bought a brand new notebook (for those interested: that's a &lt;a href="http://www.sonystyle.com/webapp/wcs/stores/servlet/CategoryDisplay?storeId=10151&amp;amp;catalogId=10551&amp;amp;langId=-1&amp;amp;categoryId=8198552921644570897&amp;amp;parentCategoryId=16154"&gt;Sony VAIO Z&lt;/a&gt;)!&lt;/li&gt;&lt;li&gt;It's taken me a long time to have DesignGridLayout tests run on both my old notebook (Windows XP) and my new one (Vista)&lt;/li&gt;&lt;li&gt;I have plaid with &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt; on my old notebook to setup a continuous build for DesignGridLayout&lt;/li&gt;&lt;/ol&gt;Now that all that works (after a lot of pain for the second point, and thanks to the active help of &lt;a href="http://alexruiz.developerblogs.com/"&gt;Alex&lt;/a&gt; for quickly improving &lt;a href="http://fest.easytesting.org/wiki/pmwiki.php"&gt;FEST&lt;/a&gt; "specially" for me), I can finally resume my work on the project.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Where do we stand?&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Version 1.2, which I am currently working on, shall have the following features:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=19"&gt;alignment synchronization of multiple panels&lt;/a&gt; (all using DesignGridLayout): particularly useful to make smooth panels transitions in Tab panes and Wizards.&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=29"&gt;optional consistent baselines spacing&lt;/a&gt; between consecutive rows (only for fixed height rows).&lt;/li&gt;&lt;li&gt;addition of a new API to notify DesignGridLayout of &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=35"&gt;special components behavior with regards to vertical resize&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;For these features, I have already made a big refactoring of the source code.&lt;br /&gt;&lt;br /&gt;Enhancement #2 above is already implemented in &lt;a href="https://designgridlayout.dev.java.net/source/browse/designgridlayout/trunk/"&gt;Subversion trunk&lt;/a&gt;, here are 2 screenshots comparing the same layout with and without consistent baseline spacing, respectively:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/Sfu46hHCfjI/AAAAAAAAAGY/AW5gehgiWAY/s1600-h/Rfe29ConsistentBaselineSpace.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 195px; height: 338px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/Sfu46hHCfjI/AAAAAAAAAGY/AW5gehgiWAY/s400/Rfe29ConsistentBaselineSpace.png" alt="" id="BLOGGER_PHOTO_ID_5331057899382930994" border="0" /&gt;&lt;/a&gt;Note the consistent spacing between labels baselines for all rows but the second one (because it has variable height) and the last one (because it has an &lt;span style="font-family:courier new;"&gt;emptyRow()&lt;/span&gt; before it to visually separate that row from the previous one).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/Sfu5csF7dKI/AAAAAAAAAGg/Lv0WufrMkLQ/s1600-h/Rfe29ExactVGaps.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 195px; height: 313px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/Sfu5csF7dKI/AAAAAAAAAGg/Lv0WufrMkLQ/s400/Rfe29ExactVGaps.png" alt="" id="BLOGGER_PHOTO_ID_5331058486446617762" border="0" /&gt;&lt;/a&gt;Without consistent baselines spacing, all vertical gaps conform to the values provided by the installed look &amp;amp; feel, but due to different heights of components and different vertical gaps, the inter-row spacing doesn't look consistent and is not very eye-friendly (this is particularly true when observing the column of labels).&lt;br /&gt;&lt;br /&gt;Regarding synchronization of alignments between different panels (enhancement #1 above), the following screenshots help showing what works today (excerpts from DesignGridLayout automatic tests).&lt;br /&gt;&lt;br /&gt;The example below is a dialog with a tabbed pane, embedding two different tabs. The first 2 screenshots show both tabs, built with DesignGridLayout, &lt;span style="font-weight: bold;"&gt;without synchronization&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qRq1w21uy-s/Sf1pvEVk6bI/AAAAAAAAAGo/-2ITJiWCaLQ/s1600-h/DGL-nosync.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 127px;" src="http://4.bp.blogspot.com/_qRq1w21uy-s/Sf1pvEVk6bI/AAAAAAAAAGo/-2ITJiWCaLQ/s400/DGL-nosync.png" alt="" id="BLOGGER_PHOTO_ID_5331533791215544754" border="0" /&gt;&lt;/a&gt;Please note that there are no horizontal or vertical alignments between both tabs, this is particularly noticeable when running the application and changing from one tab to the other.&lt;br /&gt;&lt;br /&gt;Now, let's add just one line of code to the layout building code, as follows:&lt;br /&gt;&lt;pre&gt;Synchronizer.synchronize(layout1, layout2).alignGrids().alignRows();&lt;br /&gt;&lt;/pre&gt;Then the screenshots become different (note that I have put the 2nd tab both on the right side and on the bottom of the first tab so that you can easily check alignments in both directions):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/Sf1qELC-1UI/AAAAAAAAAGw/WEsBuZcG1Ew/s1600-h/DGL-sync.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 231px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/Sf1qELC-1UI/AAAAAAAAAGw/WEsBuZcG1Ew/s400/DGL-sync.png" alt="" id="BLOGGER_PHOTO_ID_5331534153793852738" border="0" /&gt;&lt;/a&gt;You can notice the alignment of the origins of both columns of fields, and you can also check that all rows baselines from both tabs are aligned with one another.&lt;br /&gt;This example is particularly interesting due to the use of Java -default- &lt;span style="font-style: italic;"&gt;Metal &lt;/span&gt;look &amp;amp; feel, in which different kinds of components have different heights (compare the heights of &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;JComboBox&lt;/span&gt;); other look &amp;amp; feels may have consistent heights for all components, which would not show the difficulty of aligning each and every row from one tab to its matching row in the other tab. I am not sure other LayoutManagers can do that, I haven't checked yet though.&lt;br /&gt;&lt;br /&gt;Back to &lt;span style="font-weight: bold;"&gt;DesignGridLayout V1.2&lt;/span&gt; schedule, I still have some work with:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;baseline synchronization&lt;/li&gt;&lt;li&gt;new API for vertical resize behavior customization (point #3 above)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;layout calculation optimization (avoid multiple recalculation)&lt;/li&gt;&lt;/ul&gt;For &lt;span style="font-weight: bold;"&gt;DesignGridLayout 1.2&lt;/span&gt;, I plan to release a &lt;span style="font-style: italic;"&gt;beta &lt;/span&gt;with new features and the new API so that I have time to polish it before a release candidate. Here is the expected schedule:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;End May&lt;/span&gt;: 1.2 beta 1 out, beta testing will last one month&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;End June&lt;/span&gt;: 1.2 rc 1 out, followed by final release after 2 weeks (or 2 weeks after latest rc if bugs are reported)&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;What's next?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Later, for &lt;span style="font-weight: bold;"&gt;V1.3&lt;/span&gt;, I foresee enhancements to non-grid rows API &amp;amp; functionality:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;possibility to separate 2 components with unrelated gaps&lt;/li&gt;&lt;li&gt;smart positioning of standard buttons (OK, Cancel...) based on the runtime platform&lt;/li&gt;&lt;li&gt;make component sizes consistent across several rows (currently, sizes are consistent inside individual rows only)&lt;/li&gt;&lt;/ul&gt;It's hard to predict what a release date could be, but since I don't see too much complexity in these features (but as usual, the difficulty will be in finding &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-final-part.html"&gt;the right API&lt;/a&gt; for them), I think it should be possible to have a first release candidate less than 2 months after final release of V1.2, around September-October.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-9008262811770358387?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/9008262811770358387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/05/designgridlayout-where-do-we-stand.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/9008262811770358387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/9008262811770358387'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/05/designgridlayout-where-do-we-stand.html' title='DesignGridLayout: where do we stand?'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qRq1w21uy-s/Sfu46hHCfjI/AAAAAAAAAGY/AW5gehgiWAY/s72-c/Rfe29ConsistentBaselineSpace.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-2588422386083623656</id><published>2009-04-24T06:27:00.031+07:00</published><updated>2009-04-26T21:37:51.481+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bean'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='properties'/><title type='text'>Bean properties without hard-coded names?</title><content type='html'>&lt;span style="font-size:130%;"&gt;The problem&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html"&gt;Java Beans specifications&lt;/a&gt; have been around for more than a decade and, although they were good for tools (IDE), their full capabilities (bound properties in particular) were not much used in 3nd party libraries until only recently (e.g. &lt;a href="https://beansbinding.dev.java.net/"&gt;JSR-295 "Beans binding"&lt;/a&gt;, &lt;a href="http://jcp.org/en/jsr/detail?id=303"&gt;JSR-303 "Beans Validation"&lt;/a&gt;, &lt;a href="http://www.publicobject.com/glazedlists/"&gt;Glazed Lists&lt;/a&gt;...)&lt;br /&gt;&lt;br /&gt;I have never been a big fan of Bean properties for many reasons:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;they incur a lot of boilerplate code (in particular for bound properties)&lt;/li&gt;&lt;li&gt;there's no way to refer to a bean property by anything else than its name, a hard-coded &lt;span style="font-family:courier new;"&gt;String&lt;/span&gt;, hence this doesn't bear refactoring&lt;/li&gt;&lt;li&gt;they offer no compile-time safety (for the same reason as above, e.g. how can you be sure that "&lt;span style="font-family:courier new;"&gt;SSID&lt;/span&gt;" property is a &lt;span style="font-family:courier new;"&gt;String&lt;/span&gt;, it might be an &lt;span style="font-family:courier new;"&gt;int&lt;/span&gt;, or even an instance of a custom class!)&lt;/li&gt;&lt;/ol&gt;OK, I know some points are easily worked around:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Issue 1, for instance, is not a problem for most IDE: they will generate getter/setter for all your properties in no time; however, you have to remember that software maintenance cost increases with the number of lines of code to maintain (whether manually produced or automatically generated).&lt;/li&gt;&lt;li&gt;Refactoring of properties (issue 2) can be correctly handled by your IDE as well (with just some little extra effort)&lt;/li&gt;&lt;/ul&gt;Nevertheless, I see no workaround to issue 3, no IDE -as far as I know- will check that a hard-coded name refers to a property of some given type!&lt;br /&gt;&lt;br /&gt;For developers working on Swing applications in particular (but developers in other architectures may be concerned as well), beans usage is a must, so they face those issues everyday.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;The solution&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I will further demonstrate a proof of concept of how we can solve issues 2 &amp;amp; 3 above. The complete prototype also includes a solution to issue 1 (i.e. transforming "normal" bean properties into bound properties) but I won't discuss it here because many solutions have been blogged about for a couple of years. There's even an example of that in &lt;a href="http://cglib.sourceforge.net/xref/samples/Beans.html"&gt;cglib snippets&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Before getting to the solution, I'd like to describe how I came to it. It's quite simple. For unit testing, I like to use &lt;a href="http://easymock.org/"&gt;EasyMock&lt;/a&gt;. In EasyMock, here is how you create a mock and define its expectations:&lt;br /&gt;&lt;pre&gt;import static org.easymock.EasyMock.expect;&lt;br /&gt;import static org.easymock.EasyMock.createMock;&lt;br /&gt;...&lt;br /&gt;CustomerService mock = createMock(CustomerService.class);&lt;br /&gt;expect(mock.getNextAppointment()).andReturn(new Date());&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What happens here is that EasyMock generates a mock implementation of &lt;span style="font-family:courier new;"&gt;CustomerService&lt;/span&gt; interface so that any call to any method is recorded. Then the generic method &lt;span style="font-family:courier new;"&gt;expect(T)&lt;/span&gt; allows EasyMock to have compile-time safety in the &lt;span style="font-family:courier new;"&gt;andReturn(T)&lt;/span&gt; call.&lt;br /&gt;&lt;br /&gt;Hence I had the thought of using the same kind of API to get access to bean property:&lt;br /&gt;&lt;pre&gt;import static net.sf.beanutils.BeanUtils.mock;&lt;br /&gt;import static net.sf.beanutils.BeanUtils.property;&lt;br /&gt;import net.sf.beanutils.Property;&lt;br /&gt;...&lt;br /&gt;MyBean mock = mock(MyBean.class);&lt;br /&gt;Property&amp;lt;MyBean, String&amp;gt; property = property(mock.getCustomerId());&lt;br /&gt;System.out.println(property.name());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here, I would introduce a specific generic class &lt;span style="font-family:courier new;"&gt;Property&amp;lt;T, U&amp;gt;&lt;/span&gt; that encapsulates description of a given property of a specific bean class.&lt;br /&gt;&lt;br /&gt;Of course, in the example above, you've probably seen that -technically- this is different from EasyMock because EasyMock deals with &lt;span style="font-style: italic;"&gt;interfaces&lt;/span&gt; while we have to deal with beans (&lt;span style="font-style: italic;"&gt;non abstract classes&lt;/span&gt;). But there is an &lt;a href="http://easymock.org/EasyMock2_4_ClassExtension_Documentation.html"&gt;EasyMock extension&lt;/a&gt; that supports just that, it is based on &lt;a href="http://cglib.sourceforge.net/"&gt;cglib&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I have decided to also use cglib for my proof of concept, because its API seems quite easy and I found the provided examples quite straightforward.&lt;br /&gt;&lt;br /&gt;First of all, let me introduce &lt;span style="font-family:courier new;"&gt;Bean&amp;lt;T&amp;gt;&lt;/span&gt; class, which is the main factory for &lt;span style="font-family:courier new;"&gt;Property &lt;/span&gt;instances of a given bean:&lt;br /&gt;&lt;pre&gt;public class Bean&amp;lt;T&amp;gt;&lt;br /&gt;{&lt;br /&gt;    // The only way to get a Bean&amp;lt;T&amp;gt; instance is to use this factory method&lt;br /&gt;    public static &amp;lt;T&amp;gt; Bean&amp;lt;T&amp;gt; create(Class&amp;lt;T&amp;gt; clazz) {...}&lt;br /&gt;&lt;br /&gt;    // Initializes all members (uses cglib)&lt;br /&gt;    protected Bean(Class&amp;lt;T&amp;gt; clazz)&lt;br /&gt;    {&lt;br /&gt;        _clazz = clazz;&lt;br /&gt;        _properties = ReflectUtils.getBeanProperties(_clazz);&lt;br /&gt;        _mockInterceptor = new MockInterceptor(_properties);&lt;br /&gt;&lt;br /&gt;        // Create a mock immediately with cglib&lt;br /&gt;        Enhancer enhancer = new Enhancer();&lt;br /&gt;        enhancer.setSuperclass(clazz);&lt;br /&gt;        enhancer.setCallback(_mockInterceptor);&lt;br /&gt;        _mock = clazz.cast(enhancer.create());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Class&amp;lt;T&amp;gt; type() {...}&lt;br /&gt;&lt;br /&gt;    // Returns a T mock, used in conjunction with property() method (as argument)&lt;br /&gt;    public T mock() {...}&lt;br /&gt;&lt;br /&gt;    // Returns a beans property reference without using its hard-coded string name&lt;br /&gt;    // This pattern will always survive bean refactoring (compile-safe)!&lt;br /&gt;    // The returned reference can be used to get/set property value or get its&lt;br /&gt;    // name (in a safe way)&lt;br /&gt;    public&amp;lt;U&amp;gt; Property&amp;lt;T, U&amp;gt; property(U mockCall)&lt;br /&gt;    {&lt;br /&gt;        PropertyDescriptor property = _mockInterceptor.lastUsedProperty();&lt;br /&gt;        checkType(mockCall, property);&lt;br /&gt;        return Property.create(property);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Create a new bean that delegates to this one but makes all its&lt;br /&gt;    // properties bound&lt;br /&gt;    public T proxy(T source) {...}&lt;br /&gt;&lt;br /&gt;    // Global cache of Bean objects (each class T should have only one&lt;br /&gt;    // Bean&amp;lt;T&amp;gt; instance)&lt;br /&gt;    private static final Map&amp;lt;Class&amp;lt;?&amp;gt;, Bean&amp;lt;?&amp;gt;&amp;gt; _cache =&lt;br /&gt;        new HashMap&amp;lt;Class&amp;lt;?&amp;gt;, Bean&amp;lt;?&amp;gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;    private final Class&amp;lt;T&amp;gt; _clazz;&lt;br /&gt;    private final PropertyDescriptor[] _properties;&lt;br /&gt;    private final MockInterceptor _mockInterceptor;&lt;br /&gt;    private final T _mock;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Only the relevant methods &amp;amp; API are shown above. The "meat" of the code is essentially in &lt;span style="font-family:courier new;"&gt;Bean&amp;lt;T&amp;gt&lt;/span&gt; constructor and the &lt;span style="font-family:courier new;"&gt;property() &lt;/span&gt;method. The &lt;span style="font-family:courier new;"&gt;proxy()&lt;/span&gt; method is not shown here but is also interesting.&lt;br /&gt;&lt;br /&gt;The second interesting piece of code is the &lt;span style="font-family:courier new;"&gt;MockInterceptor &lt;/span&gt;class, which is automatically called by cglib for any call to a method of &lt;span style="font-family:courier new;"&gt;_mock&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;class MockInterceptor implements MethodInterceptor&lt;br /&gt;{&lt;br /&gt;    public MockInterceptor(PropertyDescriptor[] properties)&lt;br /&gt;    {&lt;br /&gt;        _properties = properties;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public PropertyDescriptor lastUsedProperty()&lt;br /&gt;    {&lt;br /&gt;        PropertyDescriptor property = _lastUsedProperty;&lt;br /&gt;        _lastUsedProperty = null;&lt;br /&gt;        return property;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Object intercept(&lt;br /&gt;        Object target, Method method, Object[] args, MethodProxy proxy)&lt;br /&gt;        throws Throwable&lt;br /&gt;    {&lt;br /&gt;        _lastUsedProperty = null;&lt;br /&gt;        // Check this is a getter&lt;br /&gt;        for (PropertyDescriptor descriptor: _properties)&lt;br /&gt;        {&lt;br /&gt;            if (method.equals(descriptor.getReadMethod()))&lt;br /&gt;            {&lt;br /&gt;                _lastUsedProperty = descriptor;&lt;br /&gt;                break;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        //TODO try to return something non-null when possible&lt;br /&gt;        //TODO should we call super method if not abstract of course)?&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private final PropertyDescriptor[] _properties;&lt;br /&gt;    //FIXME should be in a ThreadLocal no?&lt;br /&gt;    private PropertyDescriptor _lastUsedProperty = null;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The principles are quite simple actually: every time a &lt;span style="font-style: italic;"&gt;getter &lt;/span&gt;of &lt;span style="font-family:courier new;"&gt;_mock &lt;/span&gt;is called, the matching &lt;span style="font-family:courier new;"&gt;java.beans.PropertyDescriptor&lt;/span&gt; is stored in &lt;span style="font-family:courier new;"&gt;_lastUsedProperty&lt;/span&gt;. The latest is used in &lt;span style="font-family:courier new;"&gt;Bean&amp;lt;T&amp;gt;.property()&lt;/span&gt; method to create a new &lt;span style="font-family:courier new;"&gt;Property&amp;lt;T, U&amp;gt;&lt;/span&gt; instance:&lt;br /&gt;&lt;pre&gt;public class Property&amp;lt;T, U&amp;gt;&lt;br /&gt;{&lt;br /&gt;    static&amp;lt;T, U&amp;gt; Property&amp;lt;T, U&amp;gt; create(PropertyDescriptor descriptor)&lt;br /&gt;    {&lt;br /&gt;        return new Property&amp;lt;T, U&amp;gt;(descriptor);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Property(PropertyDescriptor descriptor)&lt;br /&gt;    {&lt;br /&gt;        _descriptor = descriptor;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Class&amp;lt;?&amp;gt; type() {...}&lt;br /&gt;    public U get(T bean) {...}&lt;br /&gt;    public void set(T bean, U value) {...}&lt;br /&gt;&lt;br /&gt;    public String name()&lt;br /&gt;    {&lt;br /&gt;        return _descriptor.getName();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private final PropertyDescriptor _descriptor;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, &lt;span style="font-family:courier new;"&gt;Property&amp;lt;T, U&amp;gt;&lt;/span&gt; is merely a wrapper to a &lt;span style="font-family:courier new;"&gt;java.beans.PropertyDescriptor&lt;/span&gt; instance, with additional type information (&lt;span style="font-family:courier new;"&gt;T&lt;/span&gt;: type of the bean, &lt;span style="font-family:courier new;"&gt;U&lt;/span&gt;: type of the bean property) added as generic parameters, making it typesafe.&lt;br /&gt;&lt;br /&gt;With this little design, here is what we have achieved:&lt;br /&gt;&lt;pre&gt;import static net.sf.beanutils.Bean.*;&lt;br /&gt;...&lt;br /&gt;Bean&amp;lt;MyBean&amp;gt; helper = create(MyBean.class);&lt;br /&gt;MyBean mock = mock();&lt;br /&gt;Property&amp;lt;MyBean, String&amp;gt; prop1 = helper.property(mock.getMyFirstProperty());&lt;br /&gt;Property&amp;lt;MyBean, Integer&amp;gt; prop2 = helper.property(mock.getMySecondProperty());&lt;br /&gt;...&lt;br /&gt;System.out.println(prop1.name()); // prints "myFirstProperty"&lt;br /&gt;MyBean bean = new MyBean();&lt;br /&gt;prop1.set(bean, "Something");&lt;br /&gt;prop2.set(bean, 123);&lt;br /&gt;prop1.set(bean, 123); // Compile-time error!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is not &lt;span style="font-style: italic;"&gt;exactly &lt;/span&gt;like the foreseen use shown at the beginning of this post, but that's not very far!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Evaluation &amp;amp; Limitations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All initial issues have been solved:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;no boilerplate code for bound properties (performed by &lt;span style="font-family:courier new;"&gt;Bean&amp;lt;T&amp;gt;.proxy(T source)&lt;/span&gt; method)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;no need for hard-coded property names thanks to &lt;span style="font-family:courier new;"&gt;Property&amp;lt;T, U&amp;gt;.name()&lt;/span&gt; that will return the right name&lt;/li&gt;&lt;li&gt;thanks to heavy use of Java 5 generics, this proof of concept provides compile-time safety&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;There are still some limitations with the current design:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;this works only with classes that comply to java-beans specifications (but that was a pre-requisite of the proof of concept)&lt;/li&gt;&lt;li&gt;this won't work with final classes or final getters of bean classes (limitation of cglib)&lt;/li&gt;&lt;li&gt;the current prototype is not thread-safe (that one is very easy to fix, with a &lt;span style="font-family:courier new;"&gt;ThreadLocal&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;write-only properties (no getter) are not supported (that could be added if needed)&lt;/li&gt;&lt;li&gt;type-safety may be not guaranteed in case of misuse of the API (e.g. not following the usage example shown above)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Next?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The current proof of concept just demonstrated what was feasible. Some possible next steps would be:&lt;ol&gt;&lt;li&gt;fix limitations #3 &amp;amp; #4 above (quite easy)&lt;/li&gt;&lt;li&gt;add possibility to add/remove property listeners directly in &lt;span style="font-family:courier new;"&gt;Property&amp;lt;T, U&amp;gt;&lt;/span&gt; (would ease creation of new frameworks such as binding or validation)&lt;/li&gt;&lt;li&gt;provide a work-around to limitation #2 (like logging a warning that some methods are final)&lt;/li&gt;&lt;li&gt;check that generated &lt;span style="font-family:courier new;"&gt;proxy&lt;/span&gt; can work with Hibernate and Beans-bindings&lt;/li&gt;&lt;li&gt;create an Open Source project somewhere (I thought about SourceForge which I am familiar with)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;For step #5, I am not sure I am willing to start such a project alone (I already have a bunch of other OSS projects currently and I never can find enough time for them), so if someone is interested, send me a note!&lt;br /&gt;&lt;br /&gt;You can find the complete project (maven 2 and eclipse) &lt;a href="http://jfpoilpret.googlepages.com/properties.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Any comments are appreciated. Have fun with this prototype!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-2588422386083623656?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/2588422386083623656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/04/bean-properties-without-hard-coded.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/2588422386083623656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/2588422386083623656'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/04/bean-properties-without-hard-coded.html' title='Bean properties without hard-coded names?'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-6548342036879338974</id><published>2009-02-15T19:51:00.010+07:00</published><updated>2009-02-16T20:23:52.705+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>Announce: DesignGridLayout 1.1 released!</title><content type='html'>Two weeks after the &lt;a href="http://jfpoilpret.blogspot.com/2009/01/announce-designgridlayout-11-rc1.html"&gt;fourth release candidate&lt;/a&gt; of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; 1.1 (1.1-rc4), I have decided to release the official final 1.1 release.&lt;br /&gt;&lt;br /&gt;Compared with previous official 1.0 release, this version brings the &lt;a href="http://jfpoilpret.blogspot.com/2009/01/designgridlayout-row-span-support-soon.html"&gt;multiple rows span components feature&lt;/a&gt;, in addition to a few enhancement and bugs fixes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;fixed several problems with baseline alignment (only on Java 5): issues&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=3"&gt; #3&lt;/a&gt; and &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=27"&gt;#27&lt;/a&gt;&lt;/li&gt;&lt;li&gt;fixed a problem with smart vertical resize of &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt; (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=28"&gt;issue #28&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed an exception with multi-grid feature (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=26"&gt;issue #26&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed an exception when creating a row but adding no component to it (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=30"&gt;issue #30&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed a bad look with grids having just a label but no component (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=31"&gt;issue #31&lt;/a&gt;)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;optimized the size of the example jar (removed all screenshots that are used during automatic tests)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;completely refactored the examples application&lt;/li&gt;&lt;li&gt;added an option to disable "smart vertical resize" feature (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=34"&gt;issue #34&lt;/a&gt;)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Now I can go back to work on next release (1.2). &lt;span style="font-weight: bold;"&gt;Future version 1.2 &lt;/span&gt;will have two major features:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=19"&gt;Synchronization of several layouts&lt;/a&gt;: with this feature it will be possible to ensure correct alignments (vertical or horizontal) across several panels using &lt;span style="font-family:courier new;"&gt;DesignGridLayout&lt;/span&gt;. This will be particularly useful for use inside &lt;span style="font-family:courier new;"&gt;JTabbedPane&lt;/span&gt;s or in &lt;span style="font-weight: bold;"&gt;Wizard &lt;/span&gt;dialogs.&lt;/li&gt;&lt;li&gt;Extension API to &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=35"&gt;customize policies related to components vertical resize&lt;/a&gt; (the current -internal- policy recognizes only vertical &lt;span style="font-family:courier new;"&gt;JSlider&lt;/span&gt;s and &lt;span style="font-family:courier new;"&gt;JScrollPane&lt;/span&gt;s as variable height components, but some users have expressed the need to recognize also other specialized components)&lt;/li&gt;&lt;/ol&gt;First enhancement is on the way but will take some more time since it is a quite complex feature. I intend to release a &lt;span style="font-weight: bold;"&gt;1.2-beta&lt;/span&gt; with this feature only, for users to check if it fits their needs well in synchronizing alignments. 1.2-beta is expected by mid-March if I can progress well.&lt;br /&gt;&lt;br /&gt;If you're not afraid of using a "&lt;span style="font-style: italic;"&gt;work-in-progress&lt;/span&gt;" version, you can get it from &lt;a href="https://designgridlayout.dev.java.net/source/browse/designgridlayout/"&gt;Subversion trunk&lt;/a&gt;, build it according to the &lt;a href="https://designgridlayout.dev.java.net/nonav/site/build-from-sources.html"&gt;instructions&lt;/a&gt; on the web site, then take a look at the new &lt;span style="font-family:courier new;"&gt;Synchronizer&lt;/span&gt; class.&lt;br /&gt;&lt;br /&gt;I hope to have a first 1.2 release candidate by end of March or early April.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-6548342036879338974?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/6548342036879338974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/02/announce-designgridlayout-11-released.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/6548342036879338974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/6548342036879338974'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/02/announce-designgridlayout-11-released.html' title='Announce: DesignGridLayout 1.1 released!'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-6831763684920320580</id><published>2009-02-07T09:06:00.020+07:00</published><updated>2009-02-08T11:39:48.048+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>JInternalFrame lesson learned</title><content type='html'>I am not a big fan of &lt;span style="font-weight: bold;"&gt;MDI &lt;/span&gt;(Multiple Document Interface) UI in general. I much prefer SDI or, better, UIs with docking capability.&lt;br /&gt;&lt;br /&gt;Hence I don't have much experience with Swing &lt;span style="font-family:courier new;"&gt;JDesktopPane&lt;/span&gt; &amp;amp; &lt;span style="font-family:courier new;"&gt;JInternalFrame&lt;/span&gt; (the basic building bricks for creating an &lt;span style="font-weight: bold;"&gt;MDI &lt;/span&gt;UI in Swing).&lt;br /&gt;&lt;br /&gt;However, a few days ago, an engineer from my company, developing a sample MDI UI (for an exercise related to a "GUI training"), was using &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; &lt;span style="font-family:courier new;"&gt;LayoutManager&lt;/span&gt; and found a strange behavior in one of his windows, as demonstrated in the following screenshot, taken after a few pixels increase of the frame height (note the table overlapping the "Open" button):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SY2jfR8y7TI/AAAAAAAAAFo/aIq314-wEU4/s1600-h/JInternalFrame-bug.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 272px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SY2jfR8y7TI/AAAAAAAAAFo/aIq314-wEU4/s400/JInternalFrame-bug.png" alt="" id="BLOGGER_PHOTO_ID_5300072094274612530" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The layout code is there:&lt;br /&gt;&lt;pre&gt;UIHelper.addGroup(layout, "Criteria");&lt;br /&gt;layout.row().grid(projectNameLabel).add(projectName).grid(projectNumberLabel).add(projectNumber);&lt;br /&gt;layout.row().grid(workloadMinLabel).add(workloadMin).grid(workloadMaxLabel).add(workloadMax);&lt;br /&gt;layout.row().grid(startDateFromLabel).add(startDateFrom).grid(startDateToLabel).add(startDateTo);&lt;br /&gt;layout.row().center().add(searchButton, resetButton);&lt;br /&gt;&lt;br /&gt;UIHelper.addGroup(layout, "Results");&lt;br /&gt;layout.row().center().fill().add(projectScrollPane);&lt;br /&gt;layout.row().center().add(openButton);&lt;br /&gt;&lt;/pre&gt;I found it strange because I had never faced &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=37"&gt;this bug&lt;/a&gt; with DesignGridLayout before. Hence I have started investigating. The first surprise to me was that I could not reproduce this problem in a &lt;span style="font-family:courier new;"&gt;JFrame &lt;/span&gt;or a &lt;span style="font-family:courier new;"&gt;JDialog&lt;/span&gt;!&lt;br /&gt;&lt;br /&gt;Hence I've written the simplest Test Case application, using &lt;span style="font-family:courier new;"&gt;JInternalFrame&lt;/span&gt;, to reproduce this problem:&lt;br /&gt;&lt;pre&gt;JFrame mainFrame = new JFrame();&lt;br /&gt;mainFrame.setName(getClass().getSimpleName());&lt;br /&gt;mainFrame.setBounds(30, 30, 800, 600);&lt;br /&gt;mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;br /&gt;&lt;br /&gt;JDesktopPane desktop = new JDesktopPane();&lt;br /&gt;mainFrame.setContentPane(desktop);&lt;br /&gt;mainFrame.setVisible(true);&lt;br /&gt;&lt;br /&gt;JInternalFrame frame = new JInternalFrame("", true, true, true, true);&lt;br /&gt;frame.setLocation(30, 30);&lt;br /&gt;&lt;br /&gt;JPanel top = new JPanel();&lt;br /&gt;JTable table = new JTable(CONTENTS_PLAYERS, COLUMNS_PLAYERS);&lt;br /&gt;setTableHeight(table, 3);&lt;br /&gt;DesignGridLayout layout = new DesignGridLayout(top);&lt;br /&gt;layout.row().center().fill().add(new JScrollPane(table));&lt;br /&gt;layout.row().center().add(new JButton("OK"));&lt;br /&gt;&lt;br /&gt;frame.setContentPane(top);&lt;br /&gt;frame.pack();&lt;br /&gt;desktop.add(frame);&lt;br /&gt;frame.setVisible(true);&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;    frame.setSelected(true);&lt;br /&gt;}&lt;br /&gt;catch (java.beans.PropertyVetoException e)&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Here is the screenshot (at preferred size):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SY2lsmxcxoI/AAAAAAAAAFw/W1MZXonPZbg/s1600-h/JInternalFrame-testcase.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 284px; height: 147px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SY2lsmxcxoI/AAAAAAAAAFw/W1MZXonPZbg/s400/JInternalFrame-testcase.png" alt="" id="BLOGGER_PHOTO_ID_5300074522225723010" border="0" /&gt;&lt;/a&gt;After a long debugging session, I found out that the preferred height of the &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;in the above example was incorrect at the time Swing calls &lt;span style="font-family:courier new;"&gt;LayoutManager.preferredSize()&lt;/span&gt;; but when the frame is displayed (&lt;span style="font-family:courier new;"&gt;LayoutManager.layoutContainer()&lt;/span&gt; is called), the height of the &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;is correct!&lt;br /&gt;&lt;br /&gt;Further investigation (and pixels counting on the screen!) has shown me that during the call to &lt;span style="font-family:courier new;"&gt;pack()&lt;/span&gt;, the preferred height of the &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;is equal to the preferred height of the embedded &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;, plus the &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;borders, but it does not include the &lt;span style="font-family:courier new;"&gt;JTableHeader&lt;/span&gt;!&lt;br /&gt;&lt;br /&gt;After checking Swing source code and trying to change slightly the code that packs and shows the &lt;span style="font-family:courier new;"&gt;JInternalFrame&lt;/span&gt;, I found out that the problem is that &lt;span style="font-family:courier new;"&gt;pack()&lt;/span&gt; is called too early!&lt;br /&gt;&lt;br /&gt;The following change just removed the problem:&lt;br /&gt;&lt;pre&gt;// Order of calling desktop.add() is important wrt frame.pack() call!&lt;br /&gt;desktop.add(frame);&lt;br /&gt;frame.pack();&lt;br /&gt;//desktop.add(frame);&lt;br /&gt;frame.setVisible(true);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The rant&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This problem is specified nowhere in &lt;span style="font-family:courier new;"&gt;JDesktop &lt;/span&gt;or &lt;span style="font-family:courier new;"&gt;JInternalFrame &lt;/span&gt;javadoc! In addition, the &lt;span style="font-family:courier new;"&gt;JInternalFrame &lt;/span&gt;section of the Swing tutorial is totally unclear and even misleading about that point!&lt;br /&gt;&lt;br /&gt;Well that's a lesson learned for me:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Always make sure you add &lt;/span&gt;&lt;span style="font-weight: bold;font-family:courier new;" &gt;JInternalFrame &lt;/span&gt;&lt;span style="font-weight: bold;"&gt;to the &lt;/span&gt;&lt;span style="font-weight: bold;font-family:courier new;" &gt;JDesktopPane&lt;/span&gt;&lt;span style="font-weight: bold;"&gt; before calling &lt;/span&gt;&lt;span style="font-weight: bold;font-family:courier new;" &gt;pack()&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Event better: avoid MDI like the plague (I remember, a long time ago, being allergic to Swing MDI because of various other problems...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-6831763684920320580?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/6831763684920320580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/02/jinternalframe-lesson-learned.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/6831763684920320580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/6831763684920320580'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/02/jinternalframe-lesson-learned.html' title='JInternalFrame lesson learned'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qRq1w21uy-s/SY2jfR8y7TI/AAAAAAAAAFo/aIq314-wEU4/s72-c/JInternalFrame-bug.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-7172635581548685389</id><published>2009-02-05T20:44:00.033+07:00</published><updated>2009-02-06T22:12:04.104+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='opinion'/><title type='text'>Why I do open source and how I came to it</title><content type='html'>As prompted by &lt;a href="http://www.jroller.com/alexRuiz/entry/why_i_do_open_source"&gt;Alex Ruiz&lt;/a&gt; (himself prompted by &lt;a href="http://www.pushing-pixels.org/?p=1076"&gt;Kirill&lt;/a&gt;), I will explain in this post my reasons for doing open source.&lt;br /&gt;&lt;br /&gt;But as a way to explain my motivation, I will draw a history of my involvement into open source.&lt;br /&gt;I have to admit that remembering that history was not easy, thanks Google I could find quite a lot of facts I had almost forgotten about my own past;-)&lt;br /&gt;&lt;br /&gt;Let's start with my first projects, actually never open sourced, some even never released "in the wild"!&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;ObjectBase&lt;/span&gt;: never completed (only reached specification stage in 1993)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;EasyMage&lt;/span&gt;: released in 1994 (C++, MacOS, shareware actually, source code never disclosed). At that time I developed this application as a passionate. And as a dreamer, I also hoped making some money out of it (IIRC, I did not pass over 100 licenses sold;-))&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;SCADA &lt;/span&gt;(C++ &amp;amp; Java 1.2, around year 1997-2000): never released. But this project helped me a lot learning Java along with CORBA.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;CORBA WhiteBoard &lt;/span&gt;(Java 1.4, around years 2000-2003): never released. But this project gave me more experience with Java Swing. And more impotantly, it has served as inspiration roots for my second Open Source project, HiveBoard.&lt;/li&gt;&lt;/ul&gt;All projects above have brought me a lot in terms of pure technical knowledge (C++ &amp;amp; Java languages, CORBA technologies, Swing GUIs). However, the whole process to implement these projects was quite awkward. Open Source has helped me getting more "professional" in the whole software lifecycle (in particular in the areas of automated testing, planning, issues management).&lt;br /&gt;&lt;br /&gt;Now let's go further into my progressive involvement into Open Source. First of all, all projects above were developed with a spirit of "implement everything from scratch" (the DRY principle did not really exist in these times;-) and using only little third-party -open source- libraries)&lt;br /&gt;&lt;br /&gt;My first involvement in Open Source can be traced back to 1999, it was quite limited: I have reported bugs to the &lt;a href="http://omniorb.sourceforge.net/"&gt;omniORB&lt;/a&gt; library and then contributed some answers to questions in the forum. I also evangelized omniORB to my students in 1999 (I was a part-time IT professor in Vietnam at that time).&lt;br /&gt;&lt;br /&gt;This may not look much but actually, this is very important in Open Source: contribution doesn't have to be just committing code, but also using open source code in your own projects, reporting problems, suggesting enhancements, helping other users when you can, evangelizing the products you use and like...&lt;br /&gt;&lt;br /&gt;At that period, I have also contributed problems reports to ORBACUS/Java (which was open source at that time).&lt;br /&gt;&lt;br /&gt;Then, much later on (mid 2004), at this time Spring started to take off seriously, I was interested in Dependency Injection but hated Spring because of its too verbose XML configuration. Then I discovered &lt;a href="http://hivemind.apache.org/"&gt;HiveMind&lt;/a&gt; (it was in beta at that time) and found it much more interesting than Spring.  However, it was lacking all that Spring had which is not DI-related (integration with persistence libraries in particular). I found HiveMind so good a DI framework that I wanted to evangelize it, but it always failed in comparison to Spring because of the lack of 3rd-party integration. That's why I decided, late 2004, to start &lt;a href="http://sourceforge.net/projects/hivetranse"&gt;HiveTranse&lt;/a&gt; my first OSS project. Unfortunately it seems it didn't help wider adoption of HiveMind. But at least it has served me to start my second OSS project, &lt;a href="https://sourceforge.net/projects/hiveboard/"&gt;HiveBoard&lt;/a&gt;, at the end of 2004.&lt;br /&gt;&lt;br /&gt;Both projects have really absorbed my time during several years and they have helped me improving my skills in:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Java technical area&lt;/span&gt;: I started to get acquainted with Hibernate, iBatis, securityfilter... I also worked with useful products like Tomcat and Jetty.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Software lifecycle process&lt;/span&gt;: I have learned to use tools to help me build (ant, el4ant), and test (JUnit, jmock, dbunit, abbot). I also got used to managing issues. I had to perform quite a bunch of efforts in terms of documentation (good for changing a "standard" developer's bad habits;-))&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Soft skills&lt;/span&gt;: when you commit into Open Source, you have to be careful about not growing too big an ego, and rather be humble towards the mistakes you make (and the remarks you get about these mistakes). You have to listen to what others tell you and accept criticism on your project. In other words, you have to "open up", this is one quality I believe I have grown during my Open Source years (although I am far from perfect to this regard;-))&lt;/li&gt;&lt;/ul&gt;More recently (mid 2008), I have joined the DesignGridLayout project. The reason why I did so was that because I found this library excellent, I had started to use it in HiveBoard, and I wanted to "&lt;span style="font-weight: bold;"&gt;give back&lt;/span&gt;" some of my efforts to the community, from which I had already benefited a lot (I would be very embarrassed to enumerate the exhaustive list of OSS libraries and tools I have use in the past 10 years).&lt;br /&gt;&lt;br /&gt;What I have learned from DesignGridLayout is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;how to design a good API (I believe)&lt;/li&gt;&lt;li&gt;how to "market" the project (although the results don't seem compelling so far)&lt;/li&gt;&lt;li&gt;how to keep my cool (or at least, try to;-)) when facing some "attacks" from other people&lt;/li&gt;&lt;/ul&gt;I consider the third point important because, although Open Source contribution is generally very rewarding, it can also bring you some problems you would never have imagined. I consider it a positive point however: most often, attacks arise from some fear of competition, which in a way means that what I have done so far is probably not too bad;-) Anyway, I believe the only response to attacks is "go ahead, don't listen, try to do better and show it".&lt;br /&gt;&lt;br /&gt;To sum up with the reasons I do open source:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;learn more about new technologies (very important in our profession where technology evolves at a fast pace) without any external constraint (you can choose the latest "cool stuff" to try and integrate in your work)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;learn more about tools to help the release management process (these tools I can later on push them in a professional environment in my daily job)&lt;/li&gt;&lt;li&gt;give back to the community what the community has given me&lt;br /&gt;&lt;/li&gt;&lt;li&gt;hopefully help the others by giving away something you consider useful to other people&lt;/li&gt;&lt;li&gt;get some visibility (open sourcing the projects you originally used for yourself brings some extra "cost" in terms of time, so you definitely want to have these efforts "paid back": the reward here is the kind comments on your projects, the bugs reports, the suggestions for improvements...)&lt;/li&gt;&lt;li&gt;get some "technical respect" in my daily job (engineers in my company respect me, not just because I'm older than them and I am their boss, but because they can see that I have a proven record of technical background and I am not just a stupid manager who understands nothing to software development)&lt;/li&gt;&lt;/ul&gt;To summarize further I could reduce this to just one word "&lt;span style="font-weight: bold;"&gt;Passion&lt;/span&gt;" of everything related to IT (not just coding but all tools and practices around software engineering).&lt;br /&gt;&lt;br /&gt;Actually I think that's not very far from what Kirill and Alex have mentioned: such a long post to say the same as others already have! Promised, next time I blog I'll try to talk only about new stuff;-)&lt;br /&gt;&lt;br /&gt;For those who have read until there to see who would be the next ones to be prompted for the reasons they do open source, sorry guys, you have read all my prose for nothing, I have nobody left on my list (or contrarily I have potentially too many to enumerate);-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-7172635581548685389?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/7172635581548685389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/02/why-i-do-open-source-and-how-i-came-to.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7172635581548685389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7172635581548685389'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/02/why-i-do-open-source-and-how-i-came-to.html' title='Why I do open source and how I came to it'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-4313262636463546875</id><published>2009-01-15T21:16:00.090+07:00</published><updated>2009-01-17T22:55:53.476+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>Swing UI layout: best practices</title><content type='html'>Today, I will show my best practices about designing Swing UI forms.&lt;br /&gt;&lt;br /&gt;This post focuses exclusively on &lt;span style="font-weight: bold;"&gt;UI layout&lt;/span&gt;, not on other -common- UI problems such as MVC, binding, actions... I may post about all these in future posts however.&lt;br /&gt;&lt;br /&gt;This is quite a long post, but this is partly due to screenshots showing do's and don'ts.&lt;br /&gt;&lt;br /&gt;The best practices exposed here use my &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; as an example, but most of them (if not all) should be suitable for most modern &lt;span style="font-family:courier new;"&gt;LayoutManager&lt;/span&gt;s (like &lt;span style="font-family:courier new;"&gt;GroupLayout&lt;/span&gt;) and even some old-fashioned ones (like &lt;span style="font-family:courier new;"&gt;GridBagLayout&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;For each practice, I'll show screenshots before/after the practice along with code snippets.&lt;br /&gt;&lt;br /&gt;Code presented here works with Java 5 (baseline alignment may require the additional &lt;a href="https://swing-layout.dev.java.net/"&gt;swing-layout&lt;/a&gt; library) and Java 6.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;1. Always use Baseline alignment for components that have a meaningful baseline&lt;/span&gt;&lt;br /&gt;Most modern LayoutManagers have an option to align components in a row on their baselines.&lt;br /&gt;DesignGridLayout gives you no choice: baseline alignment is automatic (and cannot be disabled).&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Without baseline alignment&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH1eH4L9tI/AAAAAAAAACY/0ScVhnLdU30/s1600-h/baseline-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 123px; height: 90px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH1eH4L9tI/AAAAAAAAACY/0ScVhnLdU30/s400/baseline-before.png" alt="" id="BLOGGER_PHOTO_ID_5292280934996637394" border="0" /&gt;&lt;/a&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;With baseline alignment&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH14lb8LFI/AAAAAAAAACg/gRBAWfuGMz0/s1600-h/baseline-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 143px; height: 105px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH14lb8LFI/AAAAAAAAACg/gRBAWfuGMz0/s400/baseline-after.png" alt="" id="BLOGGER_PHOTO_ID_5292281389607824466" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;2. Avoid using &lt;span style="font-family:courier new;"&gt;JEditorPane&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;JTextPane&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Most Swing components in Java 6 have a meaningful baseline. However, some seem to have no correct baseline (for no apparent good reason). &lt;span style="font-family:courier new;"&gt;JEditorPane&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;JTextPane&lt;/span&gt; are examples of such components. This means that it is impossible to have these components aligned on their baseline.&lt;br /&gt;Thus, when using those components, you don't know exactly how they will be aligned with other components in the same row; this will depend on the LayoutManager you use.&lt;br /&gt;DesignGridLayout aligns these components on the top of their "box", which is not very beautiful, but nothing better is possible until those components are able to return a decent baseline value.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Screenshot sample&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH2TDA_9WI/AAAAAAAAACo/zIsObPX9FrM/s1600-h/textpane-problem.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 285px; height: 115px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH2TDA_9WI/AAAAAAAAACo/zIsObPX9FrM/s400/textpane-problem.png" alt="" id="BLOGGER_PHOTO_ID_5292281844224488802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Hence the best is to not use such components at all if possible. You should prefer &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt; when it fits your needs (no need for rich text style).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;3. Call &lt;span style="font-family:courier new;"&gt;setColumns()&lt;/span&gt; on &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Most LayoutManagers use (or can use) components preferred size to perform an optimal layout.&lt;br /&gt;In Swing, many components are able to determine the optimal preferred width based on their content. For instance, &lt;span style="font-family:courier new;"&gt;JLabel&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JButton&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt; belong to this category and for these you don't need to explicitly set the preferred width.&lt;br /&gt;For &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;, however, this is not the case, by calling &lt;span style="font-family:courier new;"&gt;setColumns()&lt;/span&gt; (or using the constructor that takes a &lt;span style="font-family:courier new;"&gt;columns&lt;/span&gt; argument), you make sure these components will have the right preferred width.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Without setColumns()&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH2fyvDUzI/AAAAAAAAACw/Wr5wVRWBe_E/s1600-h/setcolumns-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 123px; height: 105px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH2fyvDUzI/AAAAAAAAACw/Wr5wVRWBe_E/s400/setcolumns-before.png" alt="" id="BLOGGER_PHOTO_ID_5292282063192544050" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With setColumns(10)&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH2sIh3xhI/AAAAAAAAAC4/c6hiTQrsGNY/s1600-h/setcolumns-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 223px; height: 105px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH2sIh3xhI/AAAAAAAAAC4/c6hiTQrsGNY/s400/setcolumns-after.png" alt="" id="BLOGGER_PHOTO_ID_5292282275201271314" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;4. Put all components that can vary in height in a &lt;span style="font-family:courier new;"&gt;JScrollPane&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Some components allow you to display several "lines" of information (&lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;...) In most cases, it is impossible to know exactly how many lines will be displayed. Hence by putting those components in a &lt;span style="font-family:courier new;"&gt;JScrollPane&lt;/span&gt; you make sure the user will be able to vertically scroll to see all available data.&lt;br /&gt;In addition, some components (such &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;) were specifically coded to be embedded in a &lt;span style="font-family:courier new;"&gt;JScrollPane&lt;/span&gt;, if you don't, they will look ugly (e.g. no border).&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Without JScrollPane&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH24w0OlTI/AAAAAAAAADA/v0oAxUorAq4/s1600-h/jscrollpane-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 223px; height: 123px;" src="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH24w0OlTI/AAAAAAAAADA/v0oAxUorAq4/s400/jscrollpane-before.png" alt="" id="BLOGGER_PHOTO_ID_5292282492174112050" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With JScrollPane&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH2_pckdbI/AAAAAAAAADI/ctjGDXE8RQw/s1600-h/jscrollpane-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 240px; height: 126px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH2_pckdbI/AAAAAAAAADI/ctjGDXE8RQw/s400/jscrollpane-after.png" alt="" id="BLOGGER_PHOTO_ID_5292282610454918578" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;5. Call &lt;span style="font-family:courier new;"&gt;setRows()&lt;/span&gt; on &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Like &lt;span style="font-family:courier new;"&gt;setColumns()&lt;/span&gt;, it will help optimize the preferred height for the component (by default its preferred height is equivalent to the height of 1 line of text).&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Without setRows()&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH3IfHITKI/AAAAAAAAADQ/1uYF1v12g_M/s1600-h/setrows-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 226px; height: 78px;" src="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH3IfHITKI/AAAAAAAAADQ/1uYF1v12g_M/s400/setrows-before.png" alt="" id="BLOGGER_PHOTO_ID_5292282762299460770" border="0" /&gt;&lt;/a&gt;Note how the &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt; looks like a simple &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt;!&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With setRows(4)&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH3PplcQcI/AAAAAAAAADY/jVhjkFCXEVg/s1600-h/setrows-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 226px; height: 126px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH3PplcQcI/AAAAAAAAADY/jVhjkFCXEVg/s400/setrows-after.png" alt="" id="BLOGGER_PHOTO_ID_5292282885370036674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;6. Call &lt;span style="font-family:courier new;"&gt;setVisibleRowCount()&lt;/span&gt; on &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;As for &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt; where you should call &lt;span style="font-family:courier new;"&gt;setRows()&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt; has a useful method that enables you to set the preferred number of rows to be visible, which will then automatically compute the preferred height (depending on the actual content of those rows).&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Without setVisibleRowCount()&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH3bzyDS3I/AAAAAAAAADg/KEGI1bI_5s8/s1600-h/setvisiblerowcount-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 155px; height: 206px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH3bzyDS3I/AAAAAAAAADg/KEGI1bI_5s8/s400/setvisiblerowcount-before.png" alt="" id="BLOGGER_PHOTO_ID_5292283094265711474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With setVisibleRowCount(4)&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH3i4FrUjI/AAAAAAAAADo/yO7STMBeh1w/s1600-h/setvisiblerowcount-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 170px; height: 134px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH3i4FrUjI/AAAAAAAAADo/yO7STMBeh1w/s400/setvisiblerowcount-after.png" alt="" id="BLOGGER_PHOTO_ID_5292283215680852530" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;7. Call &lt;span style="font-family:courier new;"&gt;setPreferredScrollableViewportSize()&lt;/span&gt; on &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Unfortunately, &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt; does not have a &lt;span style="font-family:courier new;"&gt;setVisibleRowCount()&lt;/span&gt; method as in &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt;. Hence, you need to find another way to set a preferred size (in terms of number of rows)  to the &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt; but avoid that it displays "partial" rows. The default &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt; preferred height shows 20 rows (independently of the actual number of rows in the model) which is generally more than what you would want to show.&lt;br /&gt;&lt;br /&gt;The practice I show here has worked quite well for me:&lt;br /&gt;&lt;pre&gt;static public void setTableHeight(JTable table, int rows)&lt;br /&gt;{&lt;br /&gt; int width = table.getPreferredSize().width;&lt;br /&gt; int height = rows * table.getRowHeight();&lt;br /&gt; table.setPreferredScrollableViewportSize(new Dimension(width, height));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Without setPreferredScrollableViewportSize()&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH3vBrf5-I/AAAAAAAAADw/wrWtfuKFkDQ/s1600-h/jtable-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 344px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH3vBrf5-I/AAAAAAAAADw/wrWtfuKFkDQ/s400/jtable-before.png" alt="" id="BLOGGER_PHOTO_ID_5292283424413837282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With setPreferredScrollableViewportSize()&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH33K4ZQVI/AAAAAAAAAD4/IXocl7NgYaY/s1600-h/jtable-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 346px; height: 158px;" src="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH33K4ZQVI/AAAAAAAAAD4/IXocl7NgYaY/s400/jtable-after.png" alt="" id="BLOGGER_PHOTO_ID_5292283564322799954" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;8. Force minimum width on &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;In &lt;span style="font-weight: bold;"&gt;best practice 3&lt;/span&gt; above, I have shown how to set a correct preferred width for &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt;, this will allow the &lt;span style="font-family:courier new;"&gt;LayoutManager&lt;/span&gt; to show the panel in its preferred size with correct sizes for all fields. However, when resizing the panel, the minimum size is generally used by the LayoutManager to make sure that components never shrink smaller than this minimum size.&lt;br /&gt;&lt;br /&gt;Unfortunately, &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt; minimum width is meaningless and generally needs to be set by hand to avoid ridiculously small fields when the user shrinks the panel width.&lt;br /&gt;&lt;br /&gt;However, you should always avoid setting sizes in pixels to avoid bad layouts on different kinds of monitors (you should strive to be &lt;a href="http://www.pushing-pixels.org/?p=302"&gt;resolution independent&lt;/a&gt; so that your UI will look good on low and high DPI screens).&lt;br /&gt;&lt;br /&gt;What I do is to use &lt;span style="font-family:courier new;"&gt;setColumns() &lt;/span&gt;again but as an intermediate step to setting the minimum width:&lt;br /&gt;&lt;pre&gt;static public final void setTextField(JTextField field, int min, int pref)&lt;br /&gt;{&lt;br /&gt;  field.setColumns(min);&lt;br /&gt;  field.setMinimumSize(field.getPreferredSize());&lt;br /&gt;  if (pref != min)&lt;br /&gt;  {&lt;br /&gt;      field.setColumns(pref);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;Before (trying to shrink width as much as possible)&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH4ESHlqvI/AAAAAAAAAEA/v1HDIQI5wOU/s1600-h/jtextfield-min1-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 223px; height: 105px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH4ESHlqvI/AAAAAAAAAEA/v1HDIQI5wOU/s400/jtextfield-min1-before.png" alt="" id="BLOGGER_PHOTO_ID_5292283789603875570" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qRq1w21uy-s/SXH4MJbUK1I/AAAAAAAAAEI/C-AGvac9hvE/s1600-h/jtextfield-min2-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 123px; height: 105px;" src="http://4.bp.blogspot.com/_qRq1w21uy-s/SXH4MJbUK1I/AAAAAAAAAEI/C-AGvac9hvE/s400/jtextfield-min2-before.png" alt="" id="BLOGGER_PHOTO_ID_5292283924709649234" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;After&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qRq1w21uy-s/SXH44_qt04I/AAAAAAAAAEY/klL-NBnOsI0/s1600-h/jtextfield-min2-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 164px; height: 105px;" src="http://4.bp.blogspot.com/_qRq1w21uy-s/SXH44_qt04I/AAAAAAAAAEY/klL-NBnOsI0/s400/jtextfield-min2-after.png" alt="" id="BLOGGER_PHOTO_ID_5292284695184003970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;9. Don't use TitledBorder to separate groups of information&lt;/span&gt;&lt;br /&gt;A lot of people use Swing &lt;span style="font-family:courier new;"&gt;TitledBorder&lt;/span&gt; around several sub-panels in order to separate groups of information inside a form. The major problem with this approach is that every sub-panel has its own &lt;span style="font-family:courier new;"&gt;LayoutManager&lt;/span&gt;, and LayoutManagers are disconnected from each other, hence you  are likely to have bad alignment between sub-panels:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH5J-4K4fI/AAAAAAAAAEg/9CpzoF4_0TM/s1600-h/titleborder-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 335px; height: 280px;" src="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH5J-4K4fI/AAAAAAAAAEg/9CpzoF4_0TM/s400/titleborder-before.png" alt="" id="BLOGGER_PHOTO_ID_5292284987029774834" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;If you follow &lt;a href="http://www.jgoodies.com/"&gt;Karsten Lentszch's advice&lt;/a&gt;, you could use a &lt;span style="font-family:courier new;"&gt;JLabel&lt;/span&gt; and a &lt;span style="font-family:courier new;"&gt;JSeparator&lt;/span&gt; instead:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH5VLWzvSI/AAAAAAAAAEo/2SZH4rf2Knc/s1600-h/titleborder-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 333px; height: 259px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SXH5VLWzvSI/AAAAAAAAAEo/2SZH4rf2Knc/s400/titleborder-after.png" alt="" id="BLOGGER_PHOTO_ID_5292285179358068002" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here is how you can do it with DesignGridLayout:&lt;br /&gt;&lt;pre&gt;_lblInfo.setForeground(Color.BLUE);&lt;br /&gt;layout.row().left().fill().add(_lblInfo, new JSeparator());&lt;br /&gt;layout.row().grid(_lblFirstName).add(_firstName);&lt;br /&gt;layout.row().grid(_lblSurname).add(_surname);&lt;br /&gt;&lt;br /&gt;layout.emptyRow();&lt;br /&gt;_lblOffice.setForeground(Color.BLUE);&lt;br /&gt;layout.row().left().fill().add(_lblOffice, new JSeparator());&lt;br /&gt;layout.row().grid(_lblCompany).add(_company);&lt;br /&gt;layout.row().grid(_lblAddress).add(_address);&lt;br /&gt;layout.row().grid(_lblZip).add(_zip);&lt;br /&gt;layout.row().grid(_lblCity).add(_city);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;10. Set consistent sizes for all &lt;span style="font-family:courier new;"&gt;JButton&lt;/span&gt;s in a row&lt;/span&gt;&lt;br /&gt;Swing automatically calculates &lt;span style="font-family:courier new;"&gt;JButton&lt;/span&gt; preferred size based on its content (text, icon). However, this means that all buttons in your form will have a different width!&lt;br /&gt;Depending on the LayoutManager you use, you may have to individually set the preferred sizes of all buttons, based on the preferred size of the largest one.&lt;br /&gt;&lt;br /&gt;Most modern LayoutManagers will do that for you, though. Here is an example with DesignGridLayout:&lt;br /&gt;&lt;pre&gt;layout.row().center().add(new JButton("OK"), new JButton("Cancel"));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH5jxXb-ZI/AAAAAAAAAEw/1KEVRgUaMRg/s1600-h/jbuttons-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 223px; height: 143px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SXH5jxXb-ZI/AAAAAAAAAEw/1KEVRgUaMRg/s400/jbuttons-after.png" alt="" id="BLOGGER_PHOTO_ID_5292285430079420818" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;11. Special considerations for components spanning several rows&lt;/span&gt;&lt;br /&gt;Some LayoutManagers (including DesignGridLayout) allow you to define components (like &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt; or &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;) to span several rows.&lt;br /&gt;&lt;br /&gt;When I use such components, I make sure that their preferred height (which defines the height of the &lt;span style="font-family:courier new;"&gt;JScrollPane&lt;/span&gt; in which they will be embedded) is larger than the total height of the rows that are spanned. Why so? Just a matter of taste. See for yourself:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With height smaller than spanned rows&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qRq1w21uy-s/SXH5xE_IEsI/AAAAAAAAAE4/YTsdKzXGecA/s1600-h/rowspan-before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 104px;" src="http://4.bp.blogspot.com/_qRq1w21uy-s/SXH5xE_IEsI/AAAAAAAAAE4/YTsdKzXGecA/s400/rowspan-before.png" alt="" id="BLOGGER_PHOTO_ID_5292285658684461762" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-weight: bold;"&gt;With height larger than spanned rows&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH55tuP7UI/AAAAAAAAAFA/O7Gg0_roFtw/s1600-h/rowspan-after.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 115px;" src="http://1.bp.blogspot.com/_qRq1w21uy-s/SXH55tuP7UI/AAAAAAAAAFA/O7Gg0_roFtw/s400/rowspan-after.png" alt="" id="BLOGGER_PHOTO_ID_5292285807058480450" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For some of these best practices, it may prove useful to create a class with a few helper methods (such as &lt;span style="font-family:courier new;"&gt;setTableHeight()&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;setTextField()&lt;/span&gt; above) or create a factory for your components.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;If you follow those best practices, you should achieve a better user experience in your UI forms. DesignGridLayout, if you use it, will take advantage of these best practices in an effective way.&lt;br /&gt;&lt;br /&gt;Hope that this can be useful to all Swing developers. Any comments are welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-4313262636463546875?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/4313262636463546875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/swing-ui-layout-best-practices.html#comment-form' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/4313262636463546875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/4313262636463546875'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/swing-ui-layout-best-practices.html' title='Swing UI layout: best practices'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qRq1w21uy-s/SXH1eH4L9tI/AAAAAAAAACY/0ScVhnLdU30/s72-c/baseline-before.png' height='72' width='72'/><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-9027758013414370033</id><published>2009-01-07T18:43:00.022+07:00</published><updated>2009-01-07T20:31:33.397+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>Announce: DesignGridLayout 1.1-rc1 released!</title><content type='html'>After one month of heavy work, I am proud to announce the first release candidate of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; 1.1.&lt;br /&gt;&lt;br /&gt;This version brings one major new feature and fixes a few bugs:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;new support for &lt;span style="font-weight: bold;"&gt;components to span several rows&lt;/span&gt; (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;RFE #10&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed problems with baseline alignment in &lt;span style="font-weight: bold;"&gt;JRE5&lt;/span&gt; (issues &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=3"&gt;#3&lt;/a&gt; and &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=27"&gt;#27&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed a problem with smart vertical resize of &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt; (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=28"&gt;issue #28&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed a potential exception that could occur in very specific layouts (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=26"&gt;issue #26&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;In addition, the &lt;a href="https://designgridlayout.dev.java.net/examples.jnlp"&gt;&lt;span style="font-weight: bold;"&gt;examples demo application&lt;/span&gt;&lt;/a&gt; has been completely rewritten in order to show all DesignGridLayout features along with description and source code. This application can now constitute &lt;span style="font-weight: bold;"&gt;a very effective way to learn DesignGridLayout from scratch in no time&lt;/span&gt;. It is also useful to current DesignGridLayout users who want to learn new features.&lt;br /&gt;&lt;br /&gt;The new &lt;span style="font-weight: bold;"&gt;support for components spanning multiple rows&lt;/span&gt; allows you to define layouts that look like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SWSsNNrPitI/AAAAAAAAABw/3WlX5Tf1Nko/s1600-h/RowSpan1OneList-pref-size.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 103px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SWSsNNrPitI/AAAAAAAAABw/3WlX5Tf1Nko/s400/RowSpan1OneList-pref-size.png" alt="" id="BLOGGER_PHOTO_ID_5288541205449116370" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The source code for that is quite straightforward:&lt;br /&gt;&lt;pre&gt;layout.row().grid(label1).add(field1).grid(label2).add(list);&lt;br /&gt;layout.row().grid(label3).add(field3).grid().spanRow();&lt;br /&gt;layout.row().center().add(button);&lt;br /&gt;&lt;/pre&gt;It is important to notice that "&lt;span style="font-weight: bold;"&gt;smart vertical resize&lt;/span&gt;", one of DesignGridLayout unique features, is still active on components spanning multiple rows. You can see on the following screenshots the same layout as above during vertical resize (note the list always show only entire rows and never truncates any row):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SWStTOTw68I/AAAAAAAAAB4/BMmdHArp_0I/s1600-h/RowSpan1OneList-resize-6.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 109px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SWStTOTw68I/AAAAAAAAAB4/BMmdHArp_0I/s400/RowSpan1OneList-resize-6.png" alt="" id="BLOGGER_PHOTO_ID_5288542408209918914" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SWStpUOzvSI/AAAAAAAAACA/_ffcBiM91Z8/s1600-h/RowSpan1OneList-resize-12.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 115px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SWStpUOzvSI/AAAAAAAAACA/_ffcBiM91Z8/s400/RowSpan1OneList-resize-12.png" alt="" id="BLOGGER_PHOTO_ID_5288542787756866850" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SWSuApUxg_I/AAAAAAAAACI/CaiPFItMCRw/s1600-h/RowSpan1OneList-resize-21.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 124px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SWSuApUxg_I/AAAAAAAAACI/CaiPFItMCRw/s400/RowSpan1OneList-resize-21.png" alt="" id="BLOGGER_PHOTO_ID_5288543188556022770" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qRq1w21uy-s/SWSuNEdWLlI/AAAAAAAAACQ/nOBSBjhM_8M/s1600-h/RowSpan1OneList-resize-30.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 133px;" src="http://1.bp.blogspot.com/_qRq1w21uy-s/SWSuNEdWLlI/AAAAAAAAACQ/nOBSBjhM_8M/s400/RowSpan1OneList-resize-30.png" alt="" id="BLOGGER_PHOTO_ID_5288543401998167634" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Of course, you can also see this behavior "live" if you launch the &lt;a href="https://designgridlayout.dev.java.net/examples.jnlp"&gt;examples application&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;I consider this release candidate to be ready for production and, if no bugs are reported, I expect a final release in less than one month.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-9027758013414370033?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/9027758013414370033/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/announce-designgridlayout-11-rc1.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/9027758013414370033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/9027758013414370033'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/announce-designgridlayout-11-rc1.html' title='Announce: DesignGridLayout 1.1-rc1 released!'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qRq1w21uy-s/SWSsNNrPitI/AAAAAAAAABw/3WlX5Tf1Nko/s72-c/RowSpan1OneList-pref-size.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-8689848963312063312</id><published>2009-01-02T22:17:00.003+07:00</published><updated>2009-01-02T22:36:12.943+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='new year resolutions'/><title type='text'>My resolutions for 2009</title><content type='html'>Nowadays almost everybody feels the urge to claim their private resolutions for the new year that has just started.&lt;br /&gt;&lt;br /&gt;Although I don't really feel with the same urge, I think expressing my own resolutions for 2009 in this blog will put some pressure on me to try my best following those resolutions, else one could remind these to me in one year ;-). I could also refer to this post myself to remind me what I promised myself&lt;br /&gt;&lt;br /&gt;So here they are (in no particular order):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;release 2 versions of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; (1.1 and 1.2)&lt;/li&gt;&lt;li&gt;release -finally- my &lt;a href="https://sourceforge.net/projects/guice-gui/"&gt;guice-gui&lt;/a&gt; framework&lt;/li&gt;&lt;li&gt;learn how to play the guitar at least 15 minutes per day&lt;/li&gt;&lt;li&gt;learn and play with &lt;a href="http://groovy.codehaus.org/"&gt;groovy&lt;/a&gt; (buy and read "&lt;a href="http://groovy.canoo.com/gina"&gt;Groovy in Action&lt;/a&gt;")&lt;/li&gt;&lt;li&gt;play with &lt;a href="http://grails.org/"&gt;grails&lt;/a&gt; (and write some new Open Source project with it)&lt;/li&gt;&lt;li&gt;play with &lt;a href="http://groovy.codehaus.org/Griffon"&gt;griffon&lt;/a&gt; (and see how I could possibly integrate DesignGridLayout to it;-))&lt;/li&gt;&lt;li&gt;read "&lt;a href="http://www.artima.com/shop/programming_in_scala"&gt;Programming in Scala&lt;/a&gt;"&lt;/li&gt;&lt;li&gt;spend more time with my family (this resolution is quite incompatible with all others...)&lt;/li&gt;&lt;li&gt;resume my work on &lt;a href="http://hiveboard.sourceforge.net/"&gt;HiveBoard&lt;/a&gt; and release one new version during the year&lt;/li&gt;&lt;li&gt;don't ever download JavaFx or even mention it in any of my blog posts (damn I have just broken that resolution, OK, that's the last one)&lt;/li&gt;&lt;/ul&gt;From this, you can guess more or less where I am heading for this year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-8689848963312063312?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/8689848963312063312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/my-resolutions-for-2009.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/8689848963312063312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/8689848963312063312'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/my-resolutions-for-2009.html' title='My resolutions for 2009'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-884734011029329564</id><published>2009-01-01T08:19:00.038+07:00</published><updated>2009-01-06T22:00:33.164+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>DesignGridLayout: row-span support soon ready!</title><content type='html'>December 2008 has been a busy month for me on &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Although not yet completely ready for a first 1.1 release candidate, the current &lt;a href="https://designgridlayout.dev.java.net/source/browse/designgridlayout/trunk/"&gt;Subversion trunk&lt;/a&gt; already includes some interesting changes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;added support for components spanning multiple rows (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;issue #10&lt;/a&gt;): this is further discussed below&lt;/li&gt;&lt;li&gt;fixed several problems with baseline alignment (only on Java 5): issues&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=3"&gt; #3&lt;/a&gt; and &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=27"&gt;#27&lt;/a&gt;&lt;/li&gt;&lt;li&gt;fixed a problem with smart vertical resize of &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt; (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=28"&gt;issue #28&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;fixed an exception with multi-grid feature (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=26"&gt;issue #26&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;optimized the size of the example jar (removed all screenshots that are used during automatic tests)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;completely refactored the examples application: see below&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;New showcase application&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Much of my past efforts on DesignGridLayout were spent on reworking the original examples application which was overly simplistic and not a very good way to "sell" DesignGridLayout. Hence I have written from scratch a new showdown application that:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;demonstrates all DesignGridLayout features from the basics to the more advanced uses (arranged in a tutorial-oriented way)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;describes each feature&lt;/li&gt;&lt;li&gt;always shows the relevant source code that produces every sample&lt;/li&gt;&lt;li&gt;allows you to launch the layout in a separate frame in order to test its resize behavior&lt;/li&gt;&lt;li&gt;asks you to choose your preferred Look &amp;amp; Feel so that you are not limited to the old-fashioned default Metal look &amp;amp; feel&lt;/li&gt;&lt;/ul&gt;Here is a screenshot of this new showdown application (with Windows XP look &amp;amp; feel):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SVw5yHF7zPI/AAAAAAAAABY/XhbBxHqS-f0/s1600-h/showdown-example-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 259px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SVw5yHF7zPI/AAAAAAAAABY/XhbBxHqS-f0/s400/showdown-example-1.png" alt="" id="BLOGGER_PHOTO_ID_5286163595686300914" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Now the good news is that, although there is no official release of this new DesignGridLayout application, you can already launch it today! Just click the jnlp link below:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://jfpoilpret.googlepages.com/examples-1.1-SNAPSHOT-JRE-1.6.jnlp" type="application/x-java-jnlp-file"&gt;DesignGridLayout Showdown Application (JNLP)&lt;br /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Multiple row span&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is the &lt;span style="font-weight: bold;"&gt;main improvement&lt;/span&gt; to the future DesignGridLayout 1.1.&lt;br /&gt;I had to slightly change the API (completely backward compatible with 1.0), as described in one &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-final-part.html"&gt;previous post&lt;/a&gt; on this blog. As a reminder, here is a simple example:&lt;br /&gt;&lt;pre&gt;layout.row().grid(label("lbl11")).add(field("field11")).grid(label("lbl12")).add(list());&lt;br /&gt;layout.row().grid(label("lbl21")).add(field("field21")).grid().spanRow();&lt;br /&gt;layout.row().center().add(button());&lt;/pre&gt;&lt;br /&gt;Note the &lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt; call on the second line of code. This states that "&lt;span style="font-style: italic;"&gt;in this position of the current row, span the component that is in the same position on the row above&lt;/span&gt;".&lt;br /&gt;&lt;br /&gt;The result looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SVwrrXz---I/AAAAAAAAABQ/46rRgX8qKSw/s1600-h/RowSpan1OneList-pref-size.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 103px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SVwrrXz---I/AAAAAAAAABQ/46rRgX8qKSw/s400/RowSpan1OneList-pref-size.png" alt="" id="BLOGGER_PHOTO_ID_5286148086752541666" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Please note there's a catch in this snapshot: the list size does not obey the "&lt;span style="font-style: italic;"&gt;smart vertical resize&lt;/span&gt;" feature of DesignGridLayout (first introduced &lt;a href="http://jfpoilpret.blogspot.com/2008/10/designgridlayout-real-time-resizing-of.html"&gt;here&lt;/a&gt;). That's one open point in the current source code.&lt;br /&gt;&lt;br /&gt;This open point is to decide whether the layout should keep the "smart height" for a multi row span component or not:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;if we don't keep it, then at preferred size, the bottom border of the list is aligned with the bottom border of the field (example above)&lt;/li&gt;&lt;li&gt;if we keep it, then there won't be such alignment, which may look quite ugly as in the snapshot below&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SVxFhe9bBTI/AAAAAAAAABo/a3d81vV4VU4/s1600-h/rowspan-example-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 356px; height: 289px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SVxFhe9bBTI/AAAAAAAAABo/a3d81vV4VU4/s400/rowspan-example-1.png" alt="" id="BLOGGER_PHOTO_ID_5286176504174806322" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I am currently inferring about what to do here. I wonder if this should be left as an option (new API) to the end user. Among the available options:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;disable smart vertical size for multi row span components (current situation)&lt;/li&gt;&lt;li&gt;enable smart height with loss of bottom border alignment (as in snapshot above)&lt;/li&gt;&lt;li&gt;enable smart height but keep bottom border alignment (or baseline alignment with the last visible row of the list). This means that there will be a strange vertical spacing between first and second field. I am not even sure that this is feasible actually, I'll need to investigate further.&lt;/li&gt;&lt;/ol&gt;Additional decisions would concern the form of the API to select the behavior, in particular, I have to find out a light API (that does not make the current one heavier) and which granularity to give this API, more precisely, at which level the behavior should be selectable: the whole layout, each row, or every single multi row span component? The finer the level, the heavier the API, the more complex it is to use...&lt;br /&gt;&lt;br /&gt;The behavior of &lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt; is described in the future 1.1 javadoc (not released, but you can already build it from the Subversion trunk).&lt;br /&gt;&lt;br /&gt;A second catch (not visible in the example above) with the "&lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt;" API is that, unfortunately, it is now possible to code (and compile) a layout that cannot be rendered at runtime, because it just makes no sense, as in the following example:&lt;br /&gt;&lt;pre&gt;// spanrow() called on a subgrid with different number of columns&lt;br /&gt;layout.row().grid(label("lbl1")).add(field("field1"), field("field2"), field("field3"));&lt;br /&gt;layout.row().grid(label("lbl4")).add(field("field4")).spanRow();&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt; call on the second row does not match a single component of the first row, but there are 2 candidate components to be spanned: &lt;span style="font-family:courier new;"&gt;field2&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;field3&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Since there is no way to discover this problem at compile-time, DesignGridLayout performs the check at runtime and will replace each incorrect &lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt; call with a special "&lt;span style="font-style: italic;"&gt;marker&lt;/span&gt;" component (with a tooltip that gives further information) to show that the original source code has to be reworked.&lt;br /&gt;&lt;br /&gt;The following screenshot shows many examples of incorrect API usage:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SVw6fihJZiI/AAAAAAAAABg/G6kjJMTKvmQ/s1600-h/RowSpan4ErrorMarkers.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 289px; height: 400px;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SVw6fihJZiI/AAAAAAAAABg/G6kjJMTKvmQ/s400/RowSpan4ErrorMarkers.png" alt="" id="BLOGGER_PHOTO_ID_5286164376142308898" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This screen is also available from the &lt;a href="http://jfpoilpret.googlepages.com/examples-1.1-SNAPSHOT-JRE-1.6.jnlp"&gt;showdown application&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Roadmap&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once I can progress on the open point presented above, I will be able to propose a first release candidate of version 1.1. That should be ready by mid-january hopefully. The final 1.1 version will be released 3-4 weeks after the latest stable (no open bug) release candidate.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-884734011029329564?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/884734011029329564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/designgridlayout-row-span-support-soon.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/884734011029329564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/884734011029329564'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2009/01/designgridlayout-row-span-support-soon.html' title='DesignGridLayout: row-span support soon ready!'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qRq1w21uy-s/SVw5yHF7zPI/AAAAAAAAABY/XhbBxHqS-f0/s72-c/showdown-example-1.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-4133430556774178739</id><published>2008-12-27T08:35:00.011+07:00</published><updated>2008-12-27T09:52:36.032+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaFX'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><title type='text'>Should we fork swing-layout project?</title><content type='html'>The &lt;a href="https://swing-layout.dev.java.net/"&gt;swing-layout&lt;/a&gt; project was the initial effort to bring Swing a better layout system that would in particular take into account the specificities of the installed Look &amp;amp; Feel, alongside &lt;span style="font-weight: bold;"&gt;baseline alignment support&lt;/span&gt;.&lt;br /&gt;Swing-layout works with Java5 (maybe Java1.4, I don't know, I have never checked).&lt;br /&gt;&lt;br /&gt;This is where the new Java6 &lt;a style="font-family: courier new;" href="http://java.sun.com/javase/6/docs/api/javax/swing/GroupLayout.html"&gt;GroupLayout&lt;/a&gt; has been elaborated before integration into Java6.&lt;br /&gt;&lt;br /&gt;Besides bringing a new &lt;span style="font-family: courier new;"&gt;LayoutManager&lt;/span&gt; that provides better layouts (at the expense of higher complexity in use, except if you use NetBeans Matisse designer), swing-layout also brought utilities available to other third-party layouts, in particular the aforementioned baseline support.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; is one of those &lt;span style="font-family: courier new;"&gt;LayoutManager&lt;/span&gt;s that relies on swing-layout for baseline alignment. It is also one of my Open Source projects.&lt;br /&gt;&lt;br /&gt;Swing-layout was available before Java 6 and, of course, the release of Java 6 has made it somewhat irrelevant (at least for the rare developers who could jump to Java 6 immediately).&lt;br /&gt;&lt;br /&gt;Now it is strange to discover that Java 6 baseline support is better than swing-layout itself (when using Java 5). Indeed, the swing-layout project on java.net has been left in limbo for about 2 years, and it looks nobody is really responsible for it, &lt;a href="https://swing-layout.dev.java.net/issues/buglist.cgi?Submit+query=Submit+query&amp;amp;component=swing-layout&amp;amp;issue_status=NEW&amp;amp;issue_status=STARTED&amp;amp;issue_status=REOPENED&amp;amp;email1=&amp;amp;emailtype1=exact&amp;amp;emailassigned_to1=1&amp;amp;email2=&amp;amp;emailtype2=exact&amp;amp;emailreporter2=1&amp;amp;issueidtype=include&amp;amp;issue_id=&amp;amp;changedin=&amp;amp;votes=&amp;amp;chfieldfrom=&amp;amp;chfieldto=Now&amp;amp;chfieldvalue=&amp;amp;short_desc=&amp;amp;short_desc_type=fulltext&amp;amp;long_desc=&amp;amp;long_desc_type=fulltext&amp;amp;issue_file_loc=&amp;amp;issue_file_loc_type=fulltext&amp;amp;status_whiteboard=&amp;amp;status_whiteboard_type=fulltext&amp;amp;field0-0-0=noop&amp;amp;type0-0-0=noop&amp;amp;value0-0-0=&amp;amp;cmdtype=doit"&gt;several issues&lt;/a&gt; are still open and nobody cares!&lt;br /&gt;&lt;br /&gt;So what should happen to this project? Should it definitly be buried in favor of Java 6?&lt;br /&gt;Or, stated differently:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Do we necessarily need to upgrade to Java 6 to have good Swing layouts?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I don't think so! Java 5 is still mainstream nowadays, and it is planned to reach EOSL in one year (that's still some amout of time!). So I strongly believe Java 5 users should not be left behind.&lt;br /&gt;&lt;br /&gt;Why do I talk about this topic here? As a matter of fact, I have hit &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=27"&gt;swing-layout bugs&lt;/a&gt; in DesignGridLayout and I am facing a tough decision:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Should I leave DesignGridLayout Java 5 support behind and require Java 6 as a minimum?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Or should I try my best to keep Java 5 support -with no difference in the provided features- at least for one more year?&lt;/span&gt;&lt;br /&gt;If so, how should I deal with swing-layout bugs?&lt;br /&gt;&lt;br /&gt;One idea I had was to &lt;span style="font-weight: bold;"&gt;fork the swing-layout project&lt;/span&gt; and create my own, with the same license (LGPL). Although it seemed to me a good idea, the problems I got with this is that:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I don't have the facilities to test all cases (MacOSX, Linux GTK)&lt;/li&gt;&lt;li&gt;I don't have much time left for Open Source (I already have 4 OSS projects and I can't deal with them all, I always have to put one in top priority -currently DesignGridLayout- while the others have to wait, sometimes for several months)&lt;/li&gt;&lt;li&gt;I don't want to support &lt;span style="font-family: courier new;"&gt;GroupLayout&lt;/span&gt; which I don't use and which is a competitor of my own DesignGridLayout!&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Indeed, yesterday I have refactored the whole baseline support in swing-layout (because currently everything is in one huge class with a lot of terrible code, very difficult to maintain and extend). I have fixed the two problems I have in DesignGridLayout (&lt;span style="font-family: courier new;"&gt;JScrollPane&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;JTableHeader&lt;/span&gt; baselines). This works but I don't know yet if I'll keep it (because it is LGPL and DesignGridLayout is Apache License 2) as part of DesignGridLayout source code.&lt;br /&gt;&lt;br /&gt;Thus I think that, unfortunately, I'll have to throw away this code (&lt;span style="font-style: italic;"&gt;is there someone interested out there?&lt;/span&gt;) and try to stick with the "official" swing-layout release and find some workaround that can be implemented directly in DesignGridLayout as a caller (no license incompatibility).&lt;br /&gt;&lt;br /&gt;Of course, if someone is motivated and ready to take over the effort of forking swing-layout, then I would happily give back my work to that new project. Just drop me a note.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;The rant&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Now the status of this library, sponsored by Sun, reminds me of other "currently on-going" (:-&gt;) efforts such as: &lt;span style="font-weight: bold;"&gt;JSR-295&lt;/span&gt; (&lt;a href="https://beansbinding.dev.java.net/"&gt;beans-binding&lt;/a&gt;), &lt;span style="font-weight: bold;"&gt;JSR-296&lt;/span&gt; (&lt;a href="http://appframework.dev.java.net/"&gt;Swing Application Framework&lt;/a&gt;).&lt;br /&gt;Once again, although Sun claims they don't leave Swing behind, they actually do, in favor of that half-baked JavaFX thingy, which is not even comparable to its competitors, which we may wonder if it deserves the "1.0" version number. Layout support in JavaFX made me laugh big times (it all boils down to &lt;span style="font-family: courier new;"&gt;HBox&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;VBox&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Maybe it's time to start forgetting Java (and Swing?) and learn something new (anything but JavaFX).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-4133430556774178739?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/4133430556774178739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/12/should-we-fork-swing-layout-project.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/4133430556774178739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/4133430556774178739'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/12/should-we-fork-swing-layout-project.html' title='Should we fork swing-layout project?'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-2427629062571877027</id><published>2008-12-07T19:13:00.003+07:00</published><updated>2008-12-07T19:23:28.875+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><title type='text'>Announce: DesignGridLayout 1.0 released!</title><content type='html'>One month &lt;a href="http://jfpoilpret.blogspot.com/2008/11/announce-designgridlayout-10-rc1.html"&gt;after the third release candidate&lt;/a&gt; of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; 1.0 (1.0-rc3), no new bugs being received, I have decided to release the official 1.0 version of DesignGridLayout.&lt;br /&gt;&lt;br /&gt;I trust this version to be stable and bug-free (however, if you do encounter a bug with it, do not hesitate to report it, I will be glad to provide a fix in a timely manner).&lt;br /&gt;&lt;br /&gt;For me, this means I can now start seriously working on &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;the next enhancement&lt;/a&gt; (for 1.1 version) that will allow users to define some components spanning several rows. You can already have &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-final-part.html"&gt;an overview of the future API&lt;/a&gt; (up to the current state of my reflection) if you are interested.&lt;br /&gt;&lt;br /&gt;If you have further questions about DesignGridLayout, please don't hesitate to ask in the corresponding &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectMailingListList"&gt;project lists&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-2427629062571877027?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/2427629062571877027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/12/announce-designgridlayout-10-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/2427629062571877027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/2427629062571877027'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/12/announce-designgridlayout-10-released.html' title='Announce: DesignGridLayout 1.0 released!'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-7590577041688759958</id><published>2008-11-16T16:17:00.008+07:00</published><updated>2008-11-18T20:06:18.643+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><category scheme='http://www.blogger.com/atom/ns#' term='Jazoon'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='JSR-296'/><title type='text'>My presentation on JSR-296 at Jazoon 08</title><content type='html'>I have just seen that &lt;a href="http://jazoon.com/"&gt;Jazoon&lt;/a&gt; has removed all presentations files of Jazoon 08 talks.&lt;br /&gt;That's the occasion for me to post the content of my talk on "&lt;span style="font-weight: bold;"&gt;JSR-296 Swing AppFramework from the trenches&lt;/span&gt;".&lt;br /&gt;&lt;br /&gt;The full content (in PPT format) is &lt;a href="http://jfpoilpret.googlepages.com/JSR-296-from-the-trenches-08-2008.ppt"&gt;here&lt;/a&gt; (I have just removed the Jazoon template stuff). If you don't have PowerPoint, here is a &lt;a href="http://jfpoilpret.googlepages.com/JSR-296-from-the-trenches-08-2008.pdf"&gt;PDF version&lt;/a&gt; (unfortunately without the original animations). Unfortunately I could find no way to convert it to ODP format (for OpenOffice users) without big losses in animations but also in diagrams (most of them got terribly ugly after conversion). I am afraid I'll have to give up on that one (if anyone can convert the original PPT to ODP please email me the result and I'll post it here).&lt;br /&gt;&lt;br /&gt;If you want to have an idea of the content before download, here is the agenda:&lt;br /&gt;&lt;ul&gt;&lt;li style="font-weight: bold;"&gt;Introduction&lt;/li&gt;&lt;ul&gt;&lt;li&gt;why do we need a Swing framework&lt;/li&gt;&lt;li&gt;what is JSR-296? what does it bring (in a nutshell)?&lt;/li&gt;&lt;li&gt;about the presented applications&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li style="font-weight: bold;"&gt;Application Lifecycle&lt;/li&gt;&lt;ul&gt;&lt;li&gt;standard application lifecycle&lt;/li&gt;&lt;li&gt;adding dependency injection (Spring, Hivemind, Guice)&lt;/li&gt;&lt;li&gt;adding docking capability (MyDoggy)&lt;/li&gt;&lt;li&gt;tips &amp;amp; pitfalls&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li style="font-weight: bold;"&gt;Resources &amp;amp; I18N&lt;/li&gt;&lt;ul&gt;&lt;li&gt;resource injection principles&lt;/li&gt;&lt;li&gt;what about JTable?&lt;/li&gt;&lt;li&gt;tips &amp;amp; pitfalls&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li style="font-weight: bold;"&gt;Actions &amp;amp; Tasks&lt;/li&gt;&lt;ul&gt;&lt;li&gt;a better, more responsive, javax.swing.Action&lt;/li&gt;&lt;li&gt;where should I put my @Action?&lt;/li&gt;&lt;li&gt;when should I use a Task?&lt;/li&gt;&lt;li&gt;what about Exception handling?&lt;/li&gt;&lt;li&gt;tips &amp;amp; pitfalls&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li style="font-weight: bold;"&gt;Persistent Session State&lt;/li&gt;&lt;ul&gt;&lt;li&gt;state restoration (or users' happiness)&lt;/li&gt;&lt;li&gt;what is stored? what is not? where?&lt;/li&gt;&lt;li&gt;tips &amp;amp; pitfalls&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li style="font-weight: bold;"&gt;What is missing for a complete GUI framework?&lt;/li&gt;&lt;ul&gt;&lt;li&gt;GUI framework architecture&lt;/li&gt;&lt;li&gt;Example: EL4J Swing stack&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li style="font-weight: bold;"&gt;Future&lt;/li&gt;&lt;ul&gt;&lt;li&gt;JSR-296 standpoint&lt;/li&gt;&lt;li&gt;what's likely to change?&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;Please note that, considering the recent &lt;a href="http://www.dzone.com/links/suns_layoffs_anil_gadre_and_what_happens_to_java.html"&gt;events about Sun&lt;/a&gt; in general, and &lt;a href="http://www.dzone.com/links/the_swing_sacrifice.html"&gt;Swing&lt;/a&gt; in particular, the "Future" section of the presentation is probably largely obsolete:-(&lt;br /&gt;Still, I believe that Swing is a better platform for GUI development than many others (not just talking about Java here), and developers will still use it for a couple of years more (maybe not in pure Java, though, other options exist: Groovy, Scala). Wait and see...&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-7590577041688759958?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/7590577041688759958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/my-presentation-on-jsr-296-at-jazoon-08.html#comment-form' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7590577041688759958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7590577041688759958'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/my-presentation-on-jsr-296-at-jazoon-08.html' title='My presentation on JSR-296 at Jazoon 08'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-5422838591509618690</id><published>2008-11-05T22:59:00.004+07:00</published><updated>2008-11-05T23:20:40.266+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>Announce: DesignGridLayout 1.0-rc1 released!</title><content type='html'>I am particulaly glad to announce the &lt;span style="font-weight: bold;"&gt;first release candidate of &lt;/span&gt;&lt;a style="font-weight: bold;" href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;&lt;span style="font-weight: bold;"&gt; V1.0&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;DesignGridLayout is a Swing LayoutManager, revolutionary by its API, simple but powerful. Its main advantages are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Good looking forms &lt;/span&gt;(alignment, spacing, sizing, visual balance): this is entirely taken over by DesignGridLayout itself without any special hint from the developer&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Reduced learning curve &lt;/span&gt;for developers, thanks to its fluent API which is simple, effective, compile-safe and IDE code-completion friendly&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;No graphics designer &lt;/span&gt;needed: the API &lt;span style="font-style: italic;"&gt;is &lt;/span&gt;the graphical designer&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Readability and maintainability&lt;/span&gt;: you can literally "visualize" the layout by browsing the code that sets it up; inserting a new row of components is done by simply inserting a new line of code in the layout setup code...&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Free&lt;/span&gt;: the project is open source and released under Apache License 2.0&lt;/li&gt;&lt;/ul&gt;Version 1.0-rc1 is available &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectDocumentList?folderID=10053&amp;amp;expandFolder=10053&amp;amp;folderID=0"&gt;here &lt;/a&gt;or through the &lt;a href="http://download.java.net/maven/2/"&gt;java net maven 2 repository&lt;/a&gt; (for more info, you can check my &lt;a href="http://jfpoilpret.blogspot.com/2008/09/using-designgridlayout-09-in-maven.html"&gt;previous post&lt;/a&gt; and replace "&lt;span style="font-weight: bold; font-family: courier new;"&gt;0.9&lt;/span&gt;" with "&lt;span style="font-weight: bold; font-family: courier new;"&gt;1.0-rc1&lt;/span&gt;").&lt;br /&gt;This version brings the following improvements:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=13"&gt;#13&lt;/a&gt;: support for &lt;span style="font-weight: bold;"&gt;multiple groups of fields&lt;/span&gt;, each with its own label column&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=5"&gt;#5&lt;/a&gt;: &lt;span style="font-weight: bold;"&gt;smart vertical resize&lt;/span&gt;: DesignGridLayout automatically determines which rows should grow vertically and also make sure that components height is suitable to display an entire line of information (useful for JList, JTable, JTextArea)&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=18"&gt;#18&lt;/a&gt;: &lt;span style="font-weight: bold;"&gt;smarter horizontal resize &lt;/span&gt;behavior: now DesignGridLayout won't resize components under their minimum size&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=9"&gt;#9&lt;/a&gt;: automatic &lt;span style="font-weight: bold;"&gt;support of right-to-left &lt;/span&gt;text orientation based on Locale&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=16"&gt;#16&lt;/a&gt;: &lt;span style="font-weight: bold;"&gt;smarter gaps &lt;/span&gt;for empty rows&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=15"&gt;#15&lt;/a&gt;: &lt;span style="font-weight: bold;"&gt;resolution independence&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=20"&gt;#20&lt;/a&gt;: fixed ugly layout problem when container has a border&lt;/li&gt;&lt;li&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=12"&gt;#12&lt;/a&gt;: now &lt;span style="font-family: courier new;"&gt;setLayout()&lt;/span&gt; is automatically called by &lt;span style="font-family: courier new;"&gt;DesignGridLayout &lt;/span&gt;constructor&lt;/li&gt;&lt;/ul&gt;Please note that V1.0 required API changes that I could unfortunately not keep compatible with previous 0.9 release. This should be the last time that happens (V1.1 should only &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-final-part.html"&gt;extend&lt;/a&gt; the current API).&lt;br /&gt;&lt;br /&gt;I consider the current version ready for production as the current test suite of DesignGridLayout is quite comprehensive and covers all its features.&lt;br /&gt;However, I have decided to prepare a release candidate to give myself a chance to fix any problems that users may find but that I could not discover by myself (in particular, problems related to platforms that I don't have: MacOS-X, Linux, Solaris).&lt;br /&gt;&lt;br /&gt;If needed, I will create further release candidates. I will wait about one month after a rc until I cut a final release.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;What's next?&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;first of all, I'll get some rest;-)&lt;/li&gt;&lt;li&gt;then I'll spend some weeks on my other open source project, &lt;a href="http://sourceforge.net/projects/guice-gui"&gt;guice-gui&lt;/a&gt;&lt;/li&gt;&lt;li&gt;finally I'll start working on &lt;span style="font-weight: bold;"&gt;DesignGridLayout V1.1&lt;/span&gt;, which should include just one improvement (issue &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;#10&lt;/a&gt;: support for components spanning several rows) which should be released as a Christmas present;-)&lt;/li&gt;&lt;/ul&gt;Enjoy it and don't hesitate to &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectIssues"&gt;report&lt;/a&gt; any problems or enhancements!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-5422838591509618690?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/5422838591509618690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/announce-designgridlayout-10-rc1.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5422838591509618690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5422838591509618690'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/announce-designgridlayout-10-rc1.html' title='Announce: DesignGridLayout 1.0-rc1 released!'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-3718735239829442262</id><published>2008-11-03T07:49:00.021+07:00</published><updated>2008-11-03T20:31:05.272+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='API'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>In search of the perfect API (final part)</title><content type='html'>This is the last post of my series on &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout &lt;/a&gt;API evolutions. As mentioned before, although I use DesignGridLayout to illustrate it, this series might be useful to any developer of libraries.&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://jfpoilpret.blogspot.com/2008/10/designgridlayout-in-search-of-perfect.html"&gt;first&lt;/a&gt;, &lt;a href="http://jfpoilpret.blogspot.com/2008/10/in-search-of-perfect-api-part-2.html"&gt;second&lt;/a&gt;, &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-3.html"&gt;third&lt;/a&gt; and &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-4.html"&gt;fourth&lt;/a&gt; posts, I have described the evolution of DesignGridLayout API from its original release to the latest -soon released now- V1.0.&lt;br /&gt;&lt;br /&gt;This last post is more a reflection on the next probable evolution of this API. This means that anything shown in this post has not been implemented (and might be difficult to implement), but thinking in terms of API first rather than implementation should be your mantra when writing a library with a large users base (I'm dreaming aloud;-))&lt;br /&gt;&lt;br /&gt;Immediately after releasing V1.0 (in a few days), I'll start working on V1.1, which main enhancement will be &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;issue #10 (add support for components spanning several rows)&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This is not an easy problem, API-wise; indeed, you have to keep in mind the following principles that DesignGridLayout project adheres to:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the feature (API) must be easy to understand and use&lt;/li&gt;&lt;li&gt;the code visually represents the actual layout&lt;/li&gt;&lt;li&gt;developer mistakes must be avoided or can be found (and eradicated) at compile-time&lt;/li&gt;&lt;/ul&gt;I would also add an important point here:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;don't break the current API (and the existing code that uses DesignGridLayout library V1.0)!&lt;/li&gt;&lt;/ul&gt;With this in mind, let's try to sketch what we would like the user to write (in terms of code), starting from a layout that is possible in V1.0:&lt;br /&gt;&lt;pre&gt;layout.row().grid(lbl1).add(table1).add(field2);&lt;br /&gt;layout.row().grid()    .empty()    .add(field3);&lt;br /&gt;layout.row().grid(lbl4).add(field4);&lt;br /&gt;&lt;/pre&gt;Note that &lt;span style="font-family:courier new;"&gt;table1 &lt;/span&gt;represents a &lt;span style="font-family:courier new;"&gt;JList &lt;/span&gt;in a &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;with a preferred size of a few rows (let's say 2 for this example).&lt;br /&gt;&lt;br /&gt;Here is a snapshot of the displayed layout for this snippet:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SQ736-ajHII/AAAAAAAAABI/wnKWdb6shTU/s1600-h/dgl-11-blog.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 345px; height: 150px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SQ736-ajHII/AAAAAAAAABI/wnKWdb6shTU/s400/dgl-11-blog.png" alt="" id="BLOGGER_PHOTO_ID_5264417607001513090" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here we can see that with DesignGridLayout V1.0, &lt;span style="font-family:courier new;"&gt;table1 &lt;/span&gt;is displayed at its preferred height but this fixes the height of its whole row. Hence a lot of empty space is lost on the second column of the layout. Instead, wee would like to have &lt;span style="font-family:courier new;"&gt;table1 &lt;/span&gt;span on the second row as well (that's why we have already put an &lt;span style="font-family:courier new;"&gt;empty() &lt;/span&gt;column in the same column in row 2).&lt;br /&gt;&lt;br /&gt;Now how can we let the user indicate that table1 should span on 2nd row?&lt;br /&gt;&lt;br /&gt;One option I have in mind is something like this:&lt;br /&gt;&lt;pre&gt;layout.row().grid(lbl1).add(table1).add(field2);&lt;br /&gt;layout.row().grid()    .spanRow()  .add(field3);&lt;br /&gt;layout.row().grid(lbl4).add(field4);&lt;br /&gt;&lt;/pre&gt;Using "&lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt;" on the second row would have the following meaning:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;the component on the previous row at the same column position in the same sub-grid will span this row&lt;/li&gt;&lt;li&gt;the horizontal span of this component will be the same as the column span defined for that same component on the previous row&lt;/li&gt;&lt;li&gt;the sub-grid in which &lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt; appears will have its gridspan forced to the same value as the sub-grid in the same position of the previous row&lt;/li&gt;&lt;li&gt;if the matching component in the previous row is &lt;span style="font-family:courier new;"&gt;empty()&lt;/span&gt; or &lt;span style="font-family:courier new;"&gt;empty(n)&lt;/span&gt;, then &lt;span style="font-family:courier new;"&gt;spanRow()&lt;/span&gt; is also &lt;span style="font-family:courier new;"&gt;empty() &lt;/span&gt;or &lt;span style="font-family:courier new;"&gt;empty(n)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;if this is the first row in the layout, then &lt;span style="font-family:courier new;"&gt;spanRow() &lt;/span&gt;is equivalent to &lt;span style="font-family:courier new;"&gt;empty()&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;This definition solves some problematic cases when using column spans in a grid:&lt;br /&gt;&lt;pre&gt;layout.row().grid(lbl1).add(table1, 2).add(field2);&lt;br /&gt;layout.row().grid()    .spanRow()     .add(field3);&lt;br /&gt;layout.row().grid(lbl4).add(field4);&lt;br /&gt;&lt;/pre&gt;Or even when using multiple sub-grids:&lt;br /&gt;&lt;pre&gt;layout.row().grid(lbl1, 2).add(table1).add(field2);&lt;br /&gt;layout.row().grid(1)      .spanRow().add(field3);&lt;br /&gt;layout.row().grid(lbl4)   .add(field4)    .grid(lbl5).add(field5);&lt;br /&gt;&lt;/pre&gt;However, there is a problem in this latter example: in second row, we specify a gridspan of 1, but the actual gridspan will be 2 (the same as in the first row)! This looks &lt;span style="font-weight: bold;"&gt;counter-intuitive&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;One approach to solving this problem would be to prevent the possible call to&lt;span style="font-family:courier new;"&gt; grid(int gridspan)&lt;/span&gt; or &lt;span style="font-family:courier new;"&gt;grid(JLabel label, int gridspan)&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;But the only way to prevent it would be to explicitly indicate that, when we create a new sub-grid in a row, this row will include row-spanning components, and in this case we can restrict the interface to prevent setting explicitly gridspans.&lt;br /&gt;&lt;br /&gt;For this we could simply change the signature of &lt;span style="font-family:courier new;"&gt;grid &lt;/span&gt;methods in &lt;span style="font-family:courier new;"&gt;ISubGridStarter &lt;/span&gt;interface:&lt;br /&gt;&lt;pre&gt;public interface ISubGridStarter {&lt;br /&gt;   public ISpannableGridRow grid(JLabel);&lt;br /&gt;   public ISpannableGridRow grid();&lt;br /&gt;   public IGridRow grid(JLabel, int gridspan);&lt;br /&gt;   public IGridRow grid(int gridspan);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Here we introduce a new interface &lt;span style="font-family:courier new;"&gt;ISpannableGridRow &lt;/span&gt;which is defined as follows:&lt;br /&gt;&lt;pre&gt;public interface ISpannableGridRow extends IGridRow {&lt;br /&gt;   public ISpannableGridRow spanRow();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This is just an extension of the normal &lt;span style="font-family:courier new;"&gt;IGridRow &lt;/span&gt;with one extra method that allows spanning of components.&lt;br /&gt;&lt;br /&gt;Now there is another problem (unsolved yet, else this issue would be fixed in V1.0 already;-)), related to vertical resize feature.&lt;br /&gt;DesignGridLayout has the notion of rows that can vary in height or not (based on the components they hold); in addition, it allows you to define a vertical grow weight factor for those rows with variable height (see &lt;span style="font-family:courier new;"&gt;DesignGridLayout.row(double weight)&lt;/span&gt; method).&lt;br /&gt;Now that components can possibly span several rows, how should we address the way individual rows will be shared vertical space?&lt;br /&gt;Or is this really a problem? Can't the current algorithm work as well? Only a prototype for issue #10 will tell us...&lt;br /&gt;&lt;br /&gt;For the definitive API of DesignGridLayout V1.1, well, I guess we'll have to wait until official release;-) maybe as a Christmas present, or sooner if I can manage!&lt;br /&gt;&lt;br /&gt;This post closes my long discussion on searching the best possible API for a library. Maybe, in a later post, I'll show implementation details for this API (in particular, how return co-variance (Java 5+) has  helped me much for that).&lt;br /&gt;&lt;br /&gt;Hope you found this series interesting. Don't hesitate to comment! Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-3718735239829442262?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/3718735239829442262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-final-part.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3718735239829442262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3718735239829442262'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-final-part.html' title='In search of the perfect API (final part)'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qRq1w21uy-s/SQ736-ajHII/AAAAAAAAABI/wnKWdb6shTU/s72-c/dgl-11-blog.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-3809265545339588974</id><published>2008-11-02T08:02:00.016+07:00</published><updated>2008-11-02T09:41:25.133+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='API'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>In search of the perfect API (part 4)</title><content type='html'>This is the fourth post of my series about how the API of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout &lt;/a&gt;library has been improved. This series can be useful for anyone developing libraries (DesignGridLayout just illustrates improvements).&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://jfpoilpret.blogspot.com/2008/10/designgridlayout-in-search-of-perfect.html"&gt;first post &lt;/a&gt;discussed the original V0.1 API and its related issues.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://jfpoilpret.blogspot.com/2008/10/in-search-of-perfect-api-part-2.html"&gt;second post &lt;/a&gt;has shown how to solve most V0.1 issues.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-3.html"&gt;third post &lt;/a&gt;has dealt with API improvements driven by new features. In particular, one interesting change to the API has been to introduce a new interface &lt;span style="font-family:courier new;"&gt;ISubGridStarter &lt;/span&gt;to reduce the list of available methods in a context, these methods then returning the "full" interface &lt;span style="font-family:courier new;"&gt;IGridRow&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;In this installment,  I will talk about the latest changes made to the API before V1.0 is released (which should happen very soon now).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Latest 1.0-SNAPSHOT: back to 0.1?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First of all, I was not very happy with the new methods I introduced in &lt;span style="font-family:courier new;"&gt;DesignGridLayout &lt;/span&gt;class in one of both previous snapshots:&lt;br /&gt;&lt;pre&gt;public class DesignGridLayout {&lt;br /&gt;  public INonGridRow leftRow();&lt;br /&gt;  public INonGridRow leftRow(double weight);&lt;br /&gt;  public INonGridRow rightRow();&lt;br /&gt;  public INonGridRow rightRow(double weight);&lt;br /&gt;  public INonGridRow centerRow();&lt;br /&gt;  public INonGridRow centerRow(double weight);&lt;br /&gt;  public IGridRow row();&lt;br /&gt;  public IGridRow row(double weight);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;There is a lot of repeat here. In addition, it is not clear why "&lt;span style="font-family:courier new;"&gt;row()&lt;/span&gt;" is not "&lt;span style="font-family:courier new;"&gt;gridRow()&lt;/span&gt;" (lack of consistency).&lt;br /&gt;In addition, the &lt;span style="font-family:courier new;"&gt;ISubGridStarter &lt;/span&gt;interface uses methods all named "&lt;span style="font-family:courier new;"&gt;label()&lt;/span&gt;" to actually do 2 things:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Start a new canonical sub-grid&lt;/li&gt;&lt;li&gt;Set a label to this new sub-grid (or no label at all)&lt;/li&gt;&lt;/ol&gt;So the name is not representative of the method behavior. In particular, it is extremely weird to call &lt;span style="font-family:courier new;"&gt;label()&lt;/span&gt; with no argument to indicate we start a new sub-grid with no label!&lt;br /&gt;&lt;br /&gt;Actually, I prefer something like that (in terms of usage):&lt;br /&gt;&lt;pre&gt;layout.row().left().add(...);&lt;br /&gt;layout.row(1.0).grid(lbl1).add(...).grid(lbl2).add(...);&lt;br /&gt;layout.row(0.5).grid()    .add(...).grid(lbl3).add(...);&lt;br /&gt;&lt;/pre&gt;Several changes have occurred to make this kind of code possible.&lt;br /&gt;&lt;br /&gt;First I introduced (once again) a new interface:&lt;br /&gt;&lt;pre&gt;public interface IRowCreator extends ISubGridStarter {&lt;br /&gt;  public INonGridRow left();&lt;br /&gt;  public INonGridRow right();&lt;br /&gt;  public INonGridRow center();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;I also modified &lt;span style="font-family:courier new;"&gt;ISubGridStarter &lt;/span&gt;(only the method names):&lt;br /&gt;&lt;pre&gt;public interface ISubGridStarter {&lt;br /&gt;  public IGridRow gridJLabel);&lt;br /&gt;  public IGridRow grid();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;You have to remember that &lt;span style="font-family:courier new;"&gt;IGridRow &lt;/span&gt;extends &lt;span style="font-family:courier new;"&gt;ISubGridStarter &lt;/span&gt;(which allows to later start new sub-grids in a row).&lt;br /&gt;&lt;br /&gt;Finally, I simplified &lt;span style="font-family:courier new;"&gt;DesignGridLayout &lt;/span&gt;API:&lt;br /&gt;&lt;pre&gt;public class DesignGridLayout implements LayoutManager {&lt;br /&gt;  ...&lt;br /&gt;  public IRowCreator row();&lt;br /&gt;  public IRowCreator row(double weight);&lt;br /&gt;  public void emptyRow();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now we have only 2 general methods to create a new row (whatever its type), one allows you to specify the vertical growth weight factor (the no-arg row() will automatically determine if the actual row should have variable height and, if so, assign it a default weight of 1.0).&lt;br /&gt;&lt;br /&gt;Also note that we still keep &lt;span style="font-family:courier new;"&gt;emptyRow()&lt;/span&gt;, but it has lost its "&lt;span style="font-family:courier new;"&gt;int height&lt;/span&gt;" argument, this is due to an improvement of the behavior (&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=16"&gt;issue #16: make emptyRow smarter&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Now we can code our layouts like that:&lt;br /&gt;&lt;pre&gt;layout.row().grid(lbl1).add(field1)             .grid(lbl2) .add(field2).add(field3);&lt;br /&gt;layout.row().grid()    .add(field4).add(field5) .grid(lbl3) .add(field6);&lt;br /&gt;layout.row().grid()                         .grid(lbl4) .add(field7).add(field8).add(field9);&lt;br /&gt;layout.emptyRow();&lt;br /&gt;layout.row().center().add(ok).add(cancel);&lt;br /&gt;&lt;/pre&gt;The snippet above will exhibit the following layout:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SQ0RNdSWA8I/AAAAAAAAABA/y9P0KSNDtYg/s1600-h/dgl-10-blog.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 362px; height: 169px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SQ0RNdSWA8I/AAAAAAAAABA/y9P0KSNDtYg/s400/dgl-10-blog.png" alt="" id="BLOGGER_PHOTO_ID_5263882462363517890" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The source code reflects the way the container will be laid out.&lt;br /&gt;Code completion is helpful in that it does not authorize useless, or erroneous, method calls.&lt;br /&gt;&lt;br /&gt;In fact, we have just built a simple "&lt;span style="font-style: italic;"&gt;DSL&lt;/span&gt;" for laying out a Swing container!&lt;br /&gt;&lt;br /&gt;To summarize, here is what we have gained in consistency (compared with previous 1.0-SNAPSHOT):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;you add a new row to your form with one of two methods, named the same (&lt;span style="font-family:courier new;"&gt;row()&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;once a new row has been created, you can only choose its type in a clear way (&lt;span style="font-family:courier new;"&gt;left()&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;right()&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;center()&lt;/span&gt; or&lt;span style="font-family:courier new;"&gt; grid()&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;on a grid row, you can start a new sub-grid at any time with &lt;span style="font-family:courier new;"&gt;grid()&lt;/span&gt;, this is consistent with the way you started the grid row (because a grid row starts with a sub-grid)&lt;/li&gt;&lt;/ul&gt;In addition to previous improvements:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;it is never possible for users to call a meaningless API (e.g. &lt;span style="font-family:courier new;"&gt;add(JComponent child, int span)&lt;/span&gt; in a non-grid row)&lt;/li&gt;&lt;li&gt;thus the number of options (callable methods) to the user in a context is restricted to its minimum (easing the search of methods through code completion in your IDE)&lt;/li&gt;&lt;/ul&gt;Please note that throughout this series, the API was simplified a little bit (there are a few more methods to some interfaces, but not many) in order to make it easier to follow.&lt;br /&gt;&lt;br /&gt;This API should now be considered stabilized and there should be no change until the first official release candidate &lt;span style="font-weight: bold;"&gt;1.0-rc1&lt;/span&gt; of DesignGridLayout (which should be in a few days now).&lt;br /&gt;&lt;br /&gt;In the next (and last) installment, I will talk about the future extensions of DesignGridLayout API (due for V1.1), necessary to implement the &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;issue #10 (Support for components spanning several rows)&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-3809265545339588974?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/3809265545339588974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-4.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3809265545339588974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3809265545339588974'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-4.html' title='In search of the perfect API (part 4)'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qRq1w21uy-s/SQ0RNdSWA8I/AAAAAAAAABA/y9P0KSNDtYg/s72-c/dgl-10-blog.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-6030169613797699454</id><published>2008-11-01T08:45:00.024+07:00</published><updated>2008-11-01T09:57:23.809+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='API'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>In search of the perfect API (part 3)</title><content type='html'>This is the third installment of the short series discussing how to enhance a library API to make it better for its users. The series is based on &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; as a practical example.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://jfpoilpret.blogspot.com/2008/10/designgridlayout-in-search-of-perfect.html"&gt;first post &lt;/a&gt;discussed the original API of DesignGridLayout V0.1, focusing on its weak points for the end user.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://jfpoilpret.blogspot.com/2008/10/in-search-of-perfect-api-part-2.html"&gt;second post &lt;/a&gt;introduced the API of V0.9, showing how most of V0.1 pain points were solved (in particular by using interfaces and making public only what has to be public).&lt;br /&gt;&lt;br /&gt;This third post will describe two evolutions (performed in two distinct steps) made in the current development trunk (which I call 1.0-SNAPSHOT), originated in new features requested by some users.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;First evolution to 1.0-SNAPSHOT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When implementing &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=5"&gt;issue #5 (vertical resize behavior)&lt;/a&gt;, it had to be possible to add a "vertical grow weight" factor to any kind of row (except empty rows), in the same manner as you can assign "&lt;span style="font-family:courier new;"&gt;weighty&lt;/span&gt;" in Swing &lt;span style="font-family:courier new;"&gt;GridBagLayout&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;For that, I only modified &lt;span style="font-family:courier new;"&gt;DesignGridLayout &lt;/span&gt;class:&lt;br /&gt;&lt;pre&gt;public class DesignGridLayout {&lt;br /&gt;  ...&lt;br /&gt;  public INonGridRow leftRow();&lt;br /&gt;  public INonGridRow leftRow(double weight);&lt;br /&gt;  public INonGridRow rightRow();&lt;br /&gt;  public INonGridRow rightRow(double weight);&lt;br /&gt;  public INonGridRow centerRow();&lt;br /&gt;  public INonGridRow centerRow(double weight);&lt;br /&gt;  public IGridRow row();&lt;br /&gt;  public IGridRow row(double weight);&lt;br /&gt;  public void emptyRow(int height);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;That was really straightforward and it worked well for the library user. Note that we still did not solve the API issue (existing from V0.1) allowing multiple calls to &lt;span style="font-family:courier new;"&gt;label() &lt;/span&gt;on grid rows, but where only one call is effective&lt;span style="font-weight: bold;"&gt;.&lt;/span&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;&lt;br /&gt;Second evolution of 1.0-SNAPSHOT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=13"&gt;Issue #13 (grids with multiple labels)&lt;/a&gt; was an important request from DesignGridLayout users so I had first to find the right API before implementing this feature.&lt;br /&gt;&lt;br /&gt;The first idea that came to my mind was an API that would allow the following snippet:&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).label(lbl2).add(field2);&lt;br /&gt;layout.row()            .add(field3).label(lbl4).add(field4);&lt;br /&gt;&lt;/pre&gt;So I need multiple calls to &lt;span style="font-family:courier new;"&gt;label(JLabel)&lt;/span&gt; (which was easy because the current API already enabled that, that was even its main remaining flaw).&lt;br /&gt;&lt;br /&gt;Please note the absence of a label at the beginning of the second row, which means that there will be no label at this position in the displayed form (however empty space will be added, so that &lt;span style="font-family:courier new;"&gt;field1 &lt;/span&gt;and &lt;span style="font-family:courier new;"&gt;field3 &lt;/span&gt;are vertically aligned with each other). But this possibility required to have the same possibility for the second  (and next) label in a row, I needed something like that:&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).label(lbl2).add(field2);&lt;br /&gt;layout.row()            .add(field3).label()    .add(field4);&lt;br /&gt;&lt;/pre&gt;Note the new no-arg &lt;span style="font-family:courier new;"&gt;label() &lt;/span&gt;method. Here is the new &lt;span style="font-family:courier new;"&gt;IGridRow &lt;/span&gt;interface (excerpt):&lt;br /&gt;&lt;pre&gt;public interface IGridRow {&lt;br /&gt;  ...&lt;br /&gt;  IGridRow label(JLabel);&lt;br /&gt;  IGridRow label();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Adding this method solves the problem but leads to inconsistency in the API, that we see in the snippet above, you now have two ways to obtain the same result:&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).label(lbl2).add(field2);&lt;br /&gt;layout.row()            .add(field3).label()    .add(field4);&lt;br /&gt;&lt;/pre&gt;or&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).label(lbl2).add(field2);&lt;br /&gt;layout.row().label()    .add(field3).label()    .add(field4);&lt;br /&gt;&lt;/pre&gt;The second way should actually be the only way to get this result! There are two reasons why I want that:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;API Consistency (as mentioned before)&lt;/li&gt;&lt;li&gt;It allows unclear code when creating specific layouts where one would want to completely skip the first label AND the first field (and define only the second label and field)&lt;/li&gt;&lt;/ol&gt;For example, what layout would be produced by the following snippet:&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).label(lbl2).add(field2);&lt;br /&gt;layout.row().label(lbl4).add(field4);&lt;br /&gt;&lt;/pre&gt;Should &lt;span style="font-family:courier new;"&gt;lbl4 &lt;/span&gt;belong to the first label column or the second one? If we want to avoid such inconsistencies we have to make it mandatory to call one of both &lt;span style="font-family:courier new;"&gt;label() &lt;/span&gt;methods when starting a grid row.&lt;br /&gt;&lt;br /&gt;Concretely this requires adding a new interface returned by &lt;span style="font-family:courier new;"&gt;DesignGridLayout#row()&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;public class DesignGridLayout {&lt;br /&gt;  ...&lt;br /&gt;  public ISubGridStarter row();&lt;br /&gt;  public ISubGridStarter row(double weight);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface ISubGridStarter {&lt;br /&gt;  public IGridRow label(JLabel label);&lt;br /&gt;  public IGridRow label();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;I also refactored &lt;span style="font-family:courier new;"&gt;IGridRow &lt;/span&gt;interface to extend &lt;span style="font-family:courier new;"&gt;ISubGridStarter&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;public interface IGridRow extends ISubGridStarter {&lt;br /&gt;  ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This way, we have one unique, consistent way to define our layout.&lt;br /&gt;&lt;br /&gt;In next installment, I will explain some last minute changes in the 1.0-SNAPSHOT API, in order to make it definitely better (and easier to use).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-6030169613797699454?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/6030169613797699454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/6030169613797699454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/6030169613797699454'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/11/in-search-of-perfect-api-part-3.html' title='In search of the perfect API (part 3)'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-903955564314521230</id><published>2008-10-31T18:44:00.020+07:00</published><updated>2008-11-01T09:56:55.391+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='API'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>In search of the perfect API (part 2)</title><content type='html'>This is the second installment of a sort series about the evolution of the API of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout &lt;/a&gt;library.&lt;br /&gt;Although DesignGridLayout is used as an example, this series is more general and its goal is to show how you can improve the API of any library, and what pitfalls you should avoid.&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://jfpoilpret.blogspot.com/2008/10/designgridlayout-in-search-of-perfect.html"&gt;first installment&lt;/a&gt; of this short series, we have visited the initial API of DesignGridLayout (version 0.1) and seen several drawbacks it exhibited.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Release 0.9 (heavy refactoring of both API and implementation)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When I took over the DesignGridLayout project, a few months ago, I had decided to completely refactor the library source code and make the API has good as possible; in particular, I wanted to remove all drawbacks of version 0.1.&lt;br /&gt;&lt;br /&gt;First of all, I have added interfaces in order to isolate API from the implementation details (too many public classes and methods were exposed in version 0.1).&lt;br /&gt;&lt;br /&gt;Since, in DesignGridLayout, the accessible features are different if you use:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a grid row&lt;/li&gt;&lt;li&gt;a non-grid row (center, left or right)&lt;/li&gt;&lt;li&gt;an empty row (which sole purpose is to provide some empty vertical space)&lt;/li&gt;&lt;/ul&gt;I have first decided to make different methods in DesignGridLayout class to create these various kinds of rows; one of my goals was to be able to provide a specific API according to each kind of row; in addition, deciding the type of the row at the time you create it from DesignGridLayout would remove another problem of V0.1 related to multiple changes  of the row type (which for the end user is non-sense).&lt;br /&gt;&lt;pre&gt;public class DesignGridLayout {&lt;br /&gt;   ...&lt;br /&gt;   public INonGridRow leftRow();&lt;br /&gt;   public INonGridRow rightRow();&lt;br /&gt;   public INonGridRow centerRow();&lt;br /&gt;   public IGridRow row();&lt;br /&gt;   public void emptyRow(int height);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;All names starting with &lt;span style="font-weight: bold; font-style: italic;"&gt;I&lt;/span&gt; are interfaces (I borrowed this convention from .NET, although arguable at the beginning, I found it quite interesting in my situation).&lt;br /&gt;&lt;br /&gt;Here are the interfaces (simplified, I put only the main methods for clarity):&lt;br /&gt;&lt;pre&gt;public interface INonGridRow {&lt;br /&gt;   public INonGridRow add(JComponent... children);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface IGridRow {&lt;br /&gt;   public IGridRow label(JLabel label);&lt;br /&gt;   public IGridRow add(JComponent... children);&lt;br /&gt;   public IGridRow add(JComponent child, int span);&lt;br /&gt;   public IGridRow empty();&lt;br /&gt;   public IGridRow empty(int span);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now we clearly set the list of methods available to each kind of row, excluding any unsupported method (which we didn't do in V0.1).&lt;br /&gt;&lt;br /&gt;We have also replaced the constant &lt;span style="font-family:courier new;"&gt;EMPTY &lt;/span&gt;(previously defined in &lt;span style="font-family:courier new;"&gt;Row&lt;/span&gt;) and replaced it by 2 &lt;span style="font-family:courier new;"&gt;empty()&lt;/span&gt; methods that are more explicit and help the developer (when using IDE code completion facility).&lt;br /&gt;&lt;br /&gt;With this API, we have solved most problems existing with V0.1 with one exception: one can still call &lt;span style="font-family:courier new;"&gt;label()&lt;/span&gt; several times in a grid row although it is not supported by the library.&lt;br /&gt;&lt;br /&gt;Now the example code shown in the first installment can be rewritten&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).add(field2).add(field3);&lt;br /&gt;layout.row().label(lbl2).add(field4).add(field5);&lt;br /&gt;layout.row()            .add(field6).add(field7);&lt;br /&gt;layout.emptyRow(18);&lt;br /&gt;layout.centerRow().add(ok).add(cancel);&lt;br /&gt;&lt;/pre&gt;to be compared with the previous snippet:&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).add(field2).add(field3);&lt;br /&gt;layout.row().label(lbl2).add(field4).add(field5);&lt;br /&gt;layout.row()            .add(field6).add(field7);&lt;br /&gt;layout.row().height(18);&lt;br /&gt;layout.row().center().add(ok).add(cancel);&lt;br /&gt;&lt;/pre&gt;The difference may not be remarkable here, but believe me you will see the difference when using your preferred IDE (code completion will not show you a plethora of irrelevant methods for the current context).&lt;br /&gt;&lt;br /&gt;That's it for the API of V0.9. In next installment, I will describe 2 evolutions (due to adding new features) made to the API in 2 snapshots of the future V1.0.&lt;br /&gt;&lt;br /&gt;That's it for this time. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-903955564314521230?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/903955564314521230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/in-search-of-perfect-api-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/903955564314521230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/903955564314521230'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/in-search-of-perfect-api-part-2.html' title='In search of the perfect API (part 2)'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-3878167466546245637</id><published>2008-10-30T20:28:00.023+07:00</published><updated>2008-10-30T22:03:41.442+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='API'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>DesignGridLayout: in search of the perfect API (part 1)</title><content type='html'>This is the first installment of a short series (4 or 5 posts) telling the story of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt; API.&lt;br /&gt;&lt;br /&gt;From the first beginning, DesignGridLayout was meant to be easy to use in order to build Swing forms; its API was meant to make the use of any graphical designer totally useless. It was also meant to give code maintainers the possibility to "see" the UI while only browsing the source code.&lt;br /&gt;&lt;br /&gt;For that, it has used a fluent API (some may use the term "DSL" or "Domain Specific Language" in this context) so that you could, in one line of code, define one row of your UI, eg:&lt;br /&gt;&lt;pre&gt;layout.row().label(lbl1).add(field1).add(field2).add(field3);&lt;br /&gt;layout.row().label(lbl2).add(field4).add(field5);&lt;br /&gt;layout.row()            .add(field6).add(field7);&lt;br /&gt;layout.row().height(18);&lt;br /&gt;layout.row().center().add(ok).add(cancel);&lt;/pre&gt;&lt;br /&gt;The code above would give the following result in a dialog:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SQnLYsohZCI/AAAAAAAAAA4/9-yWV5EABw4/s1600-h/dgl-01-blog.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 294px; height: 169px;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SQnLYsohZCI/AAAAAAAAAA4/9-yWV5EABw4/s400/dgl-01-blog.png" alt="" id="BLOGGER_PHOTO_ID_5262961264717554722" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;As mentioned above, this series is about the evolution of that API. Prior to giving details on API history and changes motivation, I must thank Joshua Bloch for his excellent book "&lt;span style="font-style: italic;"&gt;Effective Java, 2nd Edition&lt;/span&gt;" (I read it this summer, I had never read the first edition before) which I recommend to every Java programmer and particularly to library developers; this book has helped me a lot in thinking about providing a better API to DesignGridLayout users.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Initial release 0.1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Version 0.1 could be seen as a good prototype of implementing canonical grids in a Swing layout with a rather good API.&lt;br /&gt;&lt;br /&gt;The library was then made of 4 classes only (all public)!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class DesignGridLayout implements LayoutManager {&lt;br /&gt;    ...&lt;br /&gt;    public Row row() {...}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Row {&lt;br /&gt;    public static final JComponent EMPTY = ...;&lt;br /&gt;    public Row label(JLabel label) {...}&lt;br /&gt;    public Row add(JComponent child) {...}&lt;br /&gt;    public Row add(JComponent... children) {...}&lt;br /&gt;    public Row add(JComponent child, int span) {...}&lt;br /&gt;    public Row left() {...}&lt;br /&gt;    public Row center() {...}&lt;br /&gt;    public Row right() {...}&lt;br /&gt;    public Row grid() {...}&lt;br /&gt;    public Row height(int height) {...}&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public enum RowAlign {...}&lt;br /&gt;&lt;br /&gt;public class RowItem {...}&lt;/pre&gt;&lt;br /&gt;Quite straightforward, wasn't it? However, this API suffered from several problems (don't forget that was a kind of prototype at that time):&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Some public classes (&lt;span style="font-family:courier new;"&gt;RowItem&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;RowAlign&lt;/span&gt;) should have been hidden because they were implementation details. By exposing these classes, you make them part of the API, preventing potential future changes in the implementation (don't forget that when designing an API, you should strive to make it as good as can be, but also as narrow as possible, because it will be hard to change afterwards -without breaking compatibility).&lt;/li&gt;&lt;li&gt;Many public methods from DesignGridLayout and Row classes should have been hidden because they served no purpose to the user of the library, once again they were implementation details that were unintentionally added to the API, leading to a crippled API (in the sense that all those useless methods appear and are proposed by IDE code-completion feature)&lt;/li&gt;&lt;li&gt;This API made it possible to change the type of a row as many times as you want (by calling e.g. &lt;span style="font-family:courier new;"&gt;layout.row().grid().left().right().center().add(...);&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;You could add a label to a non-grid row (which was not supported by the implementation) so this could give wrong impressions to users of the library&lt;/li&gt;&lt;li&gt;You could add a component with a column span &gt; 1 in a non-grid row (not supported)&lt;/li&gt;&lt;li&gt;You could specify the height of any row (which is not advised for a proper look). Usage of the &lt;span style="font-family:courier new;"&gt;height(int)&lt;/span&gt; method was intended as in &lt;span style="font-family:courier new;"&gt;layout.row().height(18)&lt;/span&gt; (no component added to the row).&lt;/li&gt;&lt;/ol&gt;To summarize the main issues with this API:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;many methods or classes should not be public (they should be either private or package-private)&lt;/li&gt;&lt;li&gt;the API allows code that is not supported at runtime and may give the code writer/reader a false idea of how the UI looks like&lt;/li&gt;&lt;/ul&gt;In the next installment, I will talk about the API changes in version 0.9 (current official version of DesignGridLayout).&lt;br /&gt;&lt;br /&gt;That's all for today!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-3878167466546245637?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/3878167466546245637/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/designgridlayout-in-search-of-perfect.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3878167466546245637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3878167466546245637'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/designgridlayout-in-search-of-perfect.html' title='DesignGridLayout: in search of the perfect API (part 1)'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qRq1w21uy-s/SQnLYsohZCI/AAAAAAAAAA4/9-yWV5EABw4/s72-c/dgl-01-blog.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-3310993721682371352</id><published>2008-10-26T21:17:00.006+07:00</published><updated>2008-10-26T22:49:34.504+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><title type='text'>DesignGridLayout: final 1.0 soon released! Or not?</title><content type='html'>Today, I have finished fixing the last of the high priority issues of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;.&lt;br /&gt;All fixes are already available on &lt;a href="https://designgridlayout.dev.java.net/source/browse/designgridlayout/trunk/"&gt;Subversion trunk&lt;/a&gt;, and also as snapshots &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectDocumentList?folderID=10053"&gt;there&lt;/a&gt; (note: it is not available through maven).&lt;br /&gt;&lt;br /&gt;The past 3 weeks were quite tough in terms of issues fixing, with a lot of refactoring, improvements, and clean-up.&lt;br /&gt;&lt;br /&gt;Now I am quite happy with the current standpoint about new features:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;"&lt;span style="font-weight: bold;"&gt;smart vertical resize&lt;/span&gt;" (already discussed in &lt;a href="http://jfpoilpret.blogspot.com/2008/10/designgridlayout-real-time-resizing-of.html"&gt;this post&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;automatic support of &lt;span style="font-weight: bold;"&gt;right-to-left &lt;/span&gt;orientation Locales (discussed &lt;a href="http://jfpoilpret.blogspot.com/2008/09/flash-news-rtl-support-for.html"&gt;there&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;smarter -and simpler- API for &lt;span style="font-weight: bold;"&gt;empty rows &lt;/span&gt;(no need to specify a number of pixels: that's one step toward &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=15"&gt;resolution independence&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;better &lt;span style="font-weight: bold;"&gt;minimum size management &lt;/span&gt;(previously, DesignGridLayout would calculate a minimum size equal to the preferred size of the &lt;span style="font-family: courier new;"&gt;Container&lt;/span&gt;, but during very narrow resize, it would shrink each component to ridiculously small widths)&lt;/li&gt;&lt;li&gt;automatic call to &lt;span style="font-family: courier new;"&gt;setLayout()&lt;/span&gt; from DesignGridLayout constructor&lt;/li&gt;&lt;li&gt;last, but not least, smart support for &lt;span style="font-weight: bold;"&gt;forms with multiple label columns &lt;/span&gt;(this required some extension of the "spirit" of canonical grids, rising to the concept of "&lt;span style="font-style: italic;"&gt;canonical sub-grids&lt;/span&gt;")&lt;/li&gt;&lt;/ul&gt;The concept of "&lt;span style="font-style: italic;"&gt;multi-label grids&lt;/span&gt;" (or "&lt;span style="font-style: italic;"&gt;canonical sub-grids&lt;/span&gt;" as I often name them) still needs to be introduced on the project web site (or on this blog) but that should not be a very heavy task.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;So I consider it is soon time for an official 1.0 release!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Or is it?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I mean, there are still several &lt;a href="https://designgridlayout.dev.java.net/issues/buglist.cgi?Submit+query=Submit+query&amp;amp;component=designgridlayout&amp;amp;subcomponent=www&amp;amp;issue_status=NEW&amp;amp;issue_status=STARTED&amp;amp;issue_status=REOPENED&amp;amp;email1=&amp;amp;emailtype1=exact&amp;amp;emailassigned_to1=1&amp;amp;email2=&amp;amp;emailtype2=exact&amp;amp;emailreporter2=1&amp;amp;issueidtype=include&amp;amp;issue_id=&amp;amp;changedin=&amp;amp;votes=&amp;amp;chfieldfrom=&amp;amp;chfieldto=Now&amp;amp;chfieldvalue=&amp;amp;short_desc=&amp;amp;short_desc_type=fulltext&amp;amp;long_desc=&amp;amp;long_desc_type=fulltext&amp;amp;issue_file_loc=&amp;amp;issue_file_loc_type=fulltext&amp;amp;field0-0-0=noop&amp;amp;type0-0-0=noop&amp;amp;value0-0-0=&amp;amp;cmdtype=doit"&gt;open issues&lt;/a&gt; (enhancements only, not bugs), some of which might be worth having fixed in the 1.0 version. Of course, willing to fix these issues would then mean postponing the official 1.0 release (might be end November -or later, depending on how many issues we want solved- instead of early November).&lt;br /&gt;&lt;br /&gt;So I ask the question here (I have also asked it &lt;a href="https://designgridlayout.dev.java.net/servlets/ReadMsg?list=users&amp;amp;msgNo=13"&gt;there&lt;/a&gt; actually): what do &lt;span style="font-weight: bold;"&gt;you&lt;/span&gt;, DesignGridLayout users, prefer?&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Version 1.0 in about one week, all current issues open solved only in later versions?&lt;/li&gt;&lt;li&gt;Version 1.0 with more issues closed (which ones?), but released later (between mid November and end December, depending on which issues to solve)&lt;/li&gt;&lt;/ol&gt;If you don't know, then I'll choose on my own (but I have to admit that I am not sure about what I prefer).&lt;br /&gt;&lt;br /&gt;For the time being, even if you don't want to answer this question, you can always check the latest snapshot and report problems if you find any. Also, some of the new API method names I have chosen may be argued (I lacked inspiration for some), so don't hesitate to comment also on these (but please suggest a replacement for the new methods which name you don't like).&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-3310993721682371352?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/3310993721682371352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/designgridlayout-final-10-soon-released.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3310993721682371352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3310993721682371352'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/designgridlayout-final-10-soon-released.html' title='DesignGridLayout: final 1.0 soon released! Or not?'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-3443499034538332432</id><published>2008-10-09T07:38:00.014+07:00</published><updated>2008-10-10T07:07:22.782+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><title type='text'>DesignGridLayout: real-time resizing of JScrollPane</title><content type='html'>Hi,&lt;br /&gt;&lt;br /&gt;In my quest to solve the &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=5"&gt;issue #5&lt;/a&gt; of &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout &lt;/a&gt;(namely: "&lt;span style="font-style: italic;"&gt;Layout does not allow additional height usage after resize&lt;/span&gt;"), I have created a special dialog for testing my fixes for this issue. You can see a snapshot below (at default -ie preferred- size).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SO6b94SurvI/AAAAAAAAAAw/59lomMYAYHs/s1600-h/issue5-pref-size.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SO6b94SurvI/AAAAAAAAAAw/59lomMYAYHs/s400/issue5-pref-size.png" alt="" id="BLOGGER_PHOTO_ID_5255309302573215474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In this sample, I have put several rows, containing various kinds of components, of which some should be given extra height when the user resizes the dialog (eg &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;), and some should never grow taller than their preferred size (eg &lt;span style="font-family:courier new;"&gt;JTextField&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;I could find out that there are only 2 categories of components that want extra height when it becomes available:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;any &lt;span style="font-family:courier new;"&gt;Component &lt;/span&gt;that is set as the view of a &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;(in particular  &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;,  &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;any &lt;span style="font-family:courier new;"&gt;JSlider &lt;/span&gt;using &lt;span style="font-family:courier new;"&gt;JSlider.VERTICAL &lt;/span&gt;policy&lt;/li&gt;&lt;/ul&gt;Besides these, I did not find any component that should grow height when its embedding dialog is resized.&lt;br /&gt;&lt;br /&gt;Based on these observations, I have implemented a simple internal mechanism into DesignGridLayout for distinguishing these 2 kinds of components:&lt;br /&gt;&lt;pre&gt;interface HeightGrowPolicy&lt;br /&gt;{&lt;br /&gt;    /**&lt;br /&gt;     * Checks if a {@link Component} can grow in height.&lt;br /&gt;     * @param component the component to test&lt;br /&gt;     * @return {@code true} if {@code component} has a variable height;&lt;br /&gt;     * {@code false} if {@code component} has a fixed height.&lt;br /&gt;     */&lt;br /&gt;    public boolean canGrowHeight(Component component);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This interface is implemented by several classes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;HeightGrowPolicyMapper &lt;/span&gt;(allows to map different &lt;span style="font-family:courier new;"&gt;Component &lt;/span&gt;classes to their own specific &lt;span style="font-family:courier new;"&gt;HeightGrowPolicy&lt;/span&gt;),&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;JScrollPaneHeightGrowPolicy &lt;/span&gt;(special implementation for &lt;span style="font-family:courier new;"&gt;JScrollPane&lt;/span&gt;),&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;JSliderHeightGrowPolicy &lt;/span&gt;(special implementation for &lt;span style="font-family:courier new;"&gt;JSlider&lt;/span&gt;)&lt;/li&gt;&lt;/ul&gt;The mechanism itself is easily extensible because I can easily add further policies for other kinds of Components.&lt;br /&gt;&lt;br /&gt;My first working prototype for issue #5 was roughly that simple (of course I also had to make some little changes in a few existing classes of DesignGridLayout library).&lt;br /&gt;&lt;br /&gt;But I was not fully satisfied with it. The main reason for this was that when you extend the height of the dialog, all resizable components get a few pixels more for their height, which is &lt;span style="font-weight: bold;"&gt;absolutely ugly &lt;/span&gt;for a &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;, a &lt;span style="font-family:courier new;"&gt;JList &lt;/span&gt;or a &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;, because these components would then start to show, on the bottom side, some "incomplete" row or line of text.&lt;br /&gt;&lt;br /&gt;Whenever I see this happening in a GUI application (be it made in Java or any other language)  I generally get angry and immediately classify it in the category of "non professional" software.&lt;br /&gt;&lt;br /&gt;So I inferred on some way to solve this: that is really the &lt;span style="font-weight: bold;"&gt;responsibility of a LayoutManager &lt;/span&gt;to make sure that whenever you resize a &lt;span style="font-family:courier new;"&gt;Container&lt;/span&gt;, all resized &lt;span style="font-family:courier new;"&gt;Component&lt;/span&gt;s keep good-looking.&lt;br /&gt;&lt;br /&gt;First I have extended the interface &lt;span style="font-family:courier new;"&gt;HeightGrowPolicy &lt;/span&gt;above:&lt;br /&gt;&lt;pre&gt;interface HeightGrowPolicy&lt;br /&gt;{&lt;br /&gt;    public boolean canGrowHeight(Component component);&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Computes the maximum amount of extra height that a {@link Component} can&lt;br /&gt;     * use.&lt;br /&gt;     * @param component the component to test&lt;br /&gt;     * @param extraHeight the amount of available extra height&lt;br /&gt;     * @return the maximum amount of extra height that {@code component} can use&lt;br /&gt;     * without exceeding {@code extraHeight}&lt;br /&gt;     */&lt;br /&gt;    public int computeExtraHeight(Component component, int extraHeight);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;to give a chance to let DesignGridLayout know what amount of extra height a given &lt;span style="font-family:courier new;"&gt;Component&lt;/span&gt; will accept to keep its good look. This amount can be anything between &lt;span style="font-family:courier new;"&gt;0&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;extraHeight&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here is the implementation for &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;components:&lt;br /&gt;&lt;pre&gt;class JScrollPaneHeightGrowPolicy implements HeightGrowPolicy&lt;br /&gt;{&lt;br /&gt;    public boolean canGrowHeight(Component component)&lt;br /&gt;    {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int computeExtraHeight(Component component, int extraHeight)&lt;br /&gt;    {&lt;br /&gt;        JScrollPane scroller = (JScrollPane) component;&lt;br /&gt;        int unit = scroller.getVerticalScrollBar().getUnitIncrement(+1);&lt;br /&gt;        if (unit &amp;lt;= 0)&lt;br /&gt;        {&lt;br /&gt;            return extraHeight;&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;            // Return an integral number of units pixels&lt;br /&gt;            return (extraHeight / unit) * unit;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Simple isn't it? It makes use (behind the scenes) of the &lt;a href="http://java.sun.com/javase/6/docs/api/javax/swing/Scrollable.html"&gt;&lt;span style="font-family:courier new;"&gt;Scrollable&lt;/span&gt;&lt;/a&gt; interface that is most often implemented by components that are supposed to be used in &lt;span style="font-family:courier new;"&gt;JScrollPane &lt;/span&gt;(namely &lt;span style="font-family:courier new;"&gt;JList&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JTextArea &lt;/span&gt;and &lt;span style="font-family:courier new;"&gt;JTree&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Provided that the preferred size of these components of your dialog is already good looking, then DesignGridLayout will ensure that they always stay this way. If you want to have proper preferred size for &lt;span style="font-family:courier new;"&gt;JTable&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;JList &lt;/span&gt;or &lt;span style="font-family:courier new;"&gt;JTextArea&lt;/span&gt;, you can use specific API of these components in your own code as in the following snippet:&lt;br /&gt;&lt;pre&gt;JTextArea area = new JTextArea();&lt;br /&gt;JTable table = new JTable();&lt;br /&gt;JList list = new JList();&lt;br /&gt;&lt;br /&gt;// area has preferred height to show exactly 3 lines of text&lt;br /&gt;area.setRows(3);&lt;br /&gt;&lt;br /&gt;// table has preferred height to show exactly 4 rows&lt;br /&gt;int height = 4 * table.getRowHeight();&lt;br /&gt;table.setPreferredScrollableViewportSize(new Dimension(PREF_WIDTH, height));&lt;br /&gt;&lt;br /&gt;// list has preferred height to show exactly 2 items (ie 2 rows)&lt;br /&gt;list.setVisibleRowCount(2);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can try this on the &lt;a href="http://jfpoilpret.googlepages.com/example1.jnlp"&gt;Java Web Start enabled example&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Not bad!&lt;br /&gt;&lt;br /&gt;But that's not the end of the story yet. If you play a bit with the example above, you'll see that between two actual resize of e.g. the first &lt;span style="font-family:courier new;"&gt;JTable &lt;/span&gt;component, the spacing between that row and the next is increasing, which is in contradiction with DesignGridLayout philosophy which promises to use the "ideal" inter-components spacing (according to the LAF/platform in use).&lt;br /&gt;&lt;br /&gt;So I have made a second attempt, that you can experiment through this &lt;a href="http://jfpoilpret.googlepages.com/example2.jnlp"&gt;JWS example&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In this attempt, inter-components spacing is always preserved. However, to my viewpoint, resizing does not have a very good behavior (in real-time I mean):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;first of all, the user does not "feel" that something is going on during the first pixels of his resizing action: the layout does not change at all! The user may just stop here and think that resize does not work!&lt;/li&gt;&lt;li&gt;second, a weird behavior is observed when the exact number of extra pixels is obtained during resize: all components below the first &lt;span style="font-family:courier new;"&gt;JTable &lt;/span&gt;seem to "hop" to a new position several dozens of pixels away from their previous one!&lt;/li&gt;&lt;/ul&gt;Hence I believe I will stick with the first solution and completely remove the second one. I could make it an option for the library users to choose, but I would not feel satisfied of giving such a possibility to create layouts with such a bad feel during resize.&lt;br /&gt;&lt;br /&gt;What do you think?&lt;br /&gt;&lt;br /&gt;If you want to look at DesignGridLayout code which snippets have been used in this post, you should check out the &lt;a href="https://designgridlayout.dev.java.net/source/browse/designgridlayout/"&gt;latest trunk from subversion&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-3443499034538332432?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/3443499034538332432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/designgridlayout-real-time-resizing-of.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3443499034538332432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/3443499034538332432'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/10/designgridlayout-real-time-resizing-of.html' title='DesignGridLayout: real-time resizing of JScrollPane'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qRq1w21uy-s/SO6b94SurvI/AAAAAAAAAAw/59lomMYAYHs/s72-c/issue5-pref-size.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-7438225056006441063</id><published>2008-09-30T08:41:00.006+07:00</published><updated>2008-09-30T09:29:29.848+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><title type='text'>Flash news: RTL support for DesignGridLayout</title><content type='html'>Tonight, I have just committed into Subversion the support of right-to-left orientation for &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;. as a fix for &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=9"&gt;issue #9&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Implementation was easier than foreseen, except for the test part itself (see below).&lt;br /&gt;&lt;br /&gt;First of all, here are 2 screenshots with the same layout, but one uses LTR, the other uses RTL.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qRq1w21uy-s/SOGNoT_a2dI/AAAAAAAAAAY/zbqgfIwxI1M/s1600-h/Issue9-large-LT.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_qRq1w21uy-s/SOGNoT_a2dI/AAAAAAAAAAY/zbqgfIwxI1M/s400/Issue9-large-LT.png" alt="" id="BLOGGER_PHOTO_ID_5251634364191267282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qRq1w21uy-s/SOGN8AeK6rI/AAAAAAAAAAg/VgOksDsnIvQ/s1600-h/Issue9-large-RT.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_qRq1w21uy-s/SOGN8AeK6rI/AAAAAAAAAAg/VgOksDsnIvQ/s400/Issue9-large-RT.png" alt="" id="BLOGGER_PHOTO_ID_5251634702548921010" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;DesignGridLayout automatically discovers the orientation to be used for the container it is in charge of laying out, and simply inverts x coordinates if container is RTL-based.&lt;br /&gt;&lt;br /&gt;Determining component orientation is based on the &lt;span style="font-family:courier new;"&gt;Component#getComponentOrientation()&lt;/span&gt; method which returns a &lt;span style="font-family:courier new;"&gt;ComponentOrientation &lt;/span&gt;instance. This instance determines the orientation of the text, it is not limited to horizontal LTR (eg English) and RTL (eg Arabic) languages, but also defines languages that are written vertically (all from top to bottom) and which "lines" (should we say "columns"?) are written from right to left (eg Chinese, Japanese) or left to right (eg Mongolian).&lt;br /&gt;&lt;br /&gt;However, as far as I know, Swing components LAFs do not support components for vertical languages (I have never seen a vertical JLabel or JTextField), hence for these languages, we have to revert to the "usual" horizontal LTR orientation. For this, DesignGridLayout needs a simple trick to determine text orientation:&lt;br /&gt;&lt;pre&gt;ComponentOrientation orientation = parent.getComponentOrientation();&lt;br /&gt;boolean rtl = orientation.isHorizontal() &amp;amp;&amp;amp; !orientation.isLeftToRight();&lt;/pre&gt;&lt;br /&gt;Just using &lt;span style="font-family:courier new;"&gt;orientation.isLeftToRight()&lt;/span&gt; is not sufficient because it would render eg Japanese horizontally from right to left, which I doubt any Japanese person can read (just imagine reading English from right to left!).&lt;br /&gt;&lt;br /&gt;As mentioned above, implementing this enhancement was quite easy, but writing test cases for it was much more difficult than I expected.&lt;br /&gt;&lt;br /&gt;My original idea for tests was just to set the default Locale to one of English, Arabic, Japanese or Mongolian (in order to cover the four possible situations defined in &lt;span style="font-family:courier new;"&gt;ComponentOrientation&lt;/span&gt;). But it turned out that just creating a new Locale through &lt;span style="font-family:courier new;"&gt;new Locale("JA")&lt;/span&gt; will work only if you have this Locale installed with your JRE, else a new Locale will be instantiated but it will be unusable; in particular, &lt;span style="font-family:courier new;"&gt;ComponentOrientation.getOrientation(Locale locale)&lt;/span&gt; will not return the expected value.&lt;br /&gt;&lt;br /&gt;Since my JRE does not include Arabic, Hebrew, Japanese, Chinese and Mongolian, I had to find another way for testing. I chose to directly create a &lt;span style="font-family:courier new;"&gt;ComponentOrientation&lt;/span&gt; instance, but this is not possible since it has no public constructor and it is declared final! The only way to directly use &lt;span style="font-family:courier new;"&gt;ComponentOrientation &lt;/span&gt;is to use one of the 2 provided static instances &lt;span style="font-family:courier new;"&gt;LEFT_TO_RIGHT &lt;/span&gt;and &lt;span style="font-family:courier new;"&gt;RIGHT_TO_LEFT&lt;/span&gt;, which is what I did. But it prevented me from testing the new DesignGridLayout RTL support with vertical orientations.&lt;br /&gt;&lt;br /&gt;Hence my call to DesignGridLayout users in Japan or China (for vertical right to left) and Mongolia (for vertical left to right) for testing the latest DesignGridLayout (in subversion trunk) with their respective Locale and send me a screenshot of their results. Thanks in advance!&lt;br /&gt;&lt;br /&gt;That'll be all for today.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-7438225056006441063?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/7438225056006441063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/09/flash-news-rtl-support-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7438225056006441063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7438225056006441063'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/09/flash-news-rtl-support-for.html' title='Flash news: RTL support for DesignGridLayout'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qRq1w21uy-s/SOGNoT_a2dI/AAAAAAAAAAY/zbqgfIwxI1M/s72-c/Issue9-large-LT.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-5648964683693345293</id><published>2008-09-28T09:08:00.009+07:00</published><updated>2008-09-28T09:54:15.304+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Maven'/><title type='text'>Using DesignGridLayout 0.9 in Maven projects</title><content type='html'>Yesterday, I have &lt;a href="https://designgridlayout.dev.java.net/servlets/NewsItemView?newsItemID=6523"&gt;released&lt;/a&gt; the latest official version of &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectDocumentList?folderID=9951&amp;amp;expandFolder=9951&amp;amp;folderID=4676"&gt;DesignGridLayout&lt;/a&gt;, almost 2 years after the previous official release (!), and 4 months after being handed over the project (Jason is still project owner but he is much too busy currently).&lt;br /&gt;&lt;br /&gt;As mentioned in my &lt;a href="http://jfpoilpret.blogspot.com/2008/09/designgridlayout-news.html"&gt;previous post&lt;/a&gt;, lots of things have changed between these 2 versions.&lt;br /&gt;&lt;br /&gt;Maybe one exciting news is that now, Maven users don't have to struggle to use DesignGridLayout in their projects. Indeed, version 0.9 is now uploaded to a maven repository accessible to anyone (no more need to manually install the jar into your local repository).&lt;br /&gt;&lt;br /&gt;However, since I didn't use Maven "central" repository (the conditions for uploading an artifact there are too strict: all your dependencies &lt;span style="font-weight: bold;"&gt;must&lt;/span&gt; be present in central repo too), I wanted to briefly show how to use DesignGridLayout in your maven-based project.&lt;br /&gt;&lt;br /&gt;DesignGridLayout artifacts have been uploaded to &lt;a href="http://download.java.net/maven/2/"&gt;java.net maven repository&lt;/a&gt;. Hence you have to add this repository to your pom.xml (you may also add it to settings.xml if you prefer):&lt;br /&gt;&lt;pre&gt;&amp;lt;repositories&amp;gt;&lt;br /&gt;  &amp;lt;repository&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;maven2-repository.dev.java.net&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;name&amp;gt;Java.net Repository for Maven&amp;lt;/name&amp;gt;&lt;br /&gt;      &amp;lt;url&amp;gt;http://download.java.net/maven/2/&amp;lt;/url&amp;gt;&lt;br /&gt;      &amp;lt;layout&amp;gt;default&amp;lt;/layout&amp;gt;&lt;br /&gt;  &amp;lt;/repository&amp;gt;&lt;br /&gt;&amp;lt;/repositories&amp;gt;&lt;/pre&gt;&lt;br /&gt;Now you just have to declare the dependency as any other artifact used by your project:&lt;br /&gt;&lt;pre&gt;&amp;lt;dependencies&amp;gt;&lt;br /&gt;  &amp;lt;dependency&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;net.java.dev.designgridlayout&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;designgridlayout&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;version&amp;gt;0.9&amp;lt;/version&amp;gt;&lt;br /&gt;  &amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;/dependencies&amp;gt;&lt;/pre&gt;&lt;br /&gt;And voila! You are now ready to use DesignGridLayout for your GUI project!&lt;br /&gt;&lt;br /&gt;In addition to DesignGridLayout jar artifact, the repository also contains javadoc and source artifacts that you can automatically download, eg for your IDE (for eclipse, you can follow the instructions &lt;a href="http://maven.apache.org/plugins/maven-eclipse-plugin/examples/attach-library-sources.html"&gt;there&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Note that swing-layout.jar, the only dependency of DesignGridLayout, will be automatically downloaded to your local repository.&lt;br /&gt;&lt;br /&gt;That's it for today, enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-5648964683693345293?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/5648964683693345293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/09/using-designgridlayout-09-in-maven.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5648964683693345293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5648964683693345293'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/09/using-designgridlayout-09-in-maven.html' title='Using DesignGridLayout 0.9 in Maven projects'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-7586247026757843706</id><published>2008-09-25T10:23:00.010+07:00</published><updated>2008-09-25T13:45:01.551+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>DesignGridLayout news</title><content type='html'>Two months ago, I had &lt;a href="http://jfpoilpret.blogspot.com/2008/07/new-oss-project-responsibility.html"&gt;announced my participation&lt;/a&gt; to the OSS project &lt;a href="https://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As a brief reminder, DesignGridLayout is a LayoutManager for Java Swing GUI, which main advantages are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Good looking dialogs &lt;/span&gt;(alignment, spacing, sizing, visual balance): this is taken over by DesignGridLayout itself without any special hint from the developer&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Reduced learning curve &lt;/span&gt;for developers, thanks to the fluent API which is, at the same time, simple, effective, compile-safe (no cryptic strings to express the layout) and IDE code-completion friendly&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;No graphics designer &lt;/span&gt;needed: the API &lt;span style="font-weight: bold;"&gt;is&lt;/span&gt; the graphical designer&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Readability and maintainability&lt;/span&gt;: you can literally "&lt;span style="font-style: italic;"&gt;visualize&lt;/span&gt;" the layout by browsing the code that sets it up; inserting a new row of components is done by inserting a new line of code in your layout setup code...&lt;/li&gt;&lt;/ul&gt; During the past 2 months, after struggling with the switch from CVS to SVN, I have finally finished taking the project over.&lt;br /&gt;&lt;br /&gt;In the past few days, I have checked in the latest source code into SVN, and updated the web site.&lt;br /&gt;&lt;br /&gt;Several things have changed in this project (as compared with previous 0.1.1 version):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;License&lt;/span&gt;: the original GPL has been changed to ASL 2.0, much more open&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Build&lt;/span&gt;: now the project uses maven 2 for the build and the site generation&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Package&lt;/span&gt;: the old "zappini.designgridlayout" has been changed to a more standard "net.java.dev.designgridlayout"&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Source code&lt;/span&gt;: it has been refactored to improve the API and ease future evolutions&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Issues&lt;/span&gt;: all known bugs have been fixed&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;API&lt;/span&gt;: it has been improved on several points (more on this below) such as its narrowing (in order to prevent calls that have no effect, hence potentially pollute source code using DesignGridLayout), as well as the implementation of a few enhancements&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Javadoc&lt;/span&gt;: has been completely rewritten and completed for all public API, along with examples in the package description&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Improvements on the API consist essentially in using different interfaces for the different "rows" created by DesignGridLayout:&lt;br /&gt;&lt;ul&gt;&lt;li style="font-weight: bold;"&gt;IRow&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;IGridRow &lt;/span&gt;extends IRow&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;INonGridRow &lt;/span&gt;extends IRow&lt;/li&gt;&lt;/ul&gt;These interfaces define the exact methods available to each kind of row. Once a row has been created, you cannot change its type (e.g. from Grid to Center) as you could before, which was useless, required more calls and led to some flaws in the API (e.g. what should happen if you set the row type to Grid, then back to Center; and why would you do that?).&lt;br /&gt;&lt;br /&gt;Moreover, in DesignGridLayout, methods that create rows have been specialized to determine upfront which kind of row is to be created:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;DesignGridLayout#row() &lt;/span&gt;creates a grid row (IGridRow)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;DesignGridLayout#centerRow() &lt;/span&gt;creates a non grid row, with centered components (INonGridRow)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;DesignGridLayout#leftRow() &lt;/span&gt;creates a non grid row, with left-aligned components (INonGridRow)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;DesignGridLayout#rightRow() &lt;/span&gt;creates a non grid row, with right-aligned components (INonGridRow)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;DesignGridLayout#emptyRow(int height) &lt;/span&gt;creates an empty row, with no component at all (used for introducing vertical spacing between rows)&lt;/li&gt;&lt;/ul&gt;   Other changes in the API are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Removal of IGridRow#label(String) &lt;/span&gt;to keep only IGridRow#label(JLabel): this was motivated by the fact a LayoutManager should not create components by itself (arguable opinion, I admit); in addition, this reduces one's options for GUI i18n (one option is to use Component#getName()) to set its text, which is impossible here, since the end-developer code can not get hold of the created JLabel)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Row.EMPTY &lt;/span&gt;"component" is replaced by &lt;span style="font-weight: bold;"&gt;IGridRow#empty()&lt;/span&gt; methods&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;New INonGridRow#fill() &lt;/span&gt;added to allow extreme components to take all remaining space in the row. This is particularly useful to split groups of rows with a label and a separator (as in Karsten Lentzsch FormLayout)&lt;/li&gt;&lt;li&gt;New &lt;span style="font-weight: bold;"&gt;IRow#addMulti()&lt;/span&gt; method to add several components in only one grid column, which is useful when you have components that should always "stick together" (eg a JSpinner and a JLabel indicating a unit of measure)&lt;/li&gt;&lt;/ul&gt;With all these changes, all constants and enum have been removed from the previous version because they serve no purpose now.&lt;br /&gt;&lt;br /&gt;You can find the current snapshot (named "&lt;span style="font-weight: bold;"&gt;0.9-SNAPSHOT&lt;/span&gt;") of this version &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectDocumentList?folderID=9951&amp;amp;expandFolder=9951&amp;amp;folderID=0"&gt;there&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So a further question is "&lt;span style="font-style: italic;"&gt;when will the official &lt;span style="font-weight: bold;"&gt;0.9 version&lt;/span&gt; be released?&lt;/span&gt;". That should be short now, we should expect an official release by mid October, including uploaded artifacts to some maven repository (for developers using maven).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;What's the &lt;span style="font-weight: bold;"&gt;roadmap for 1.0&lt;/span&gt;?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There are a couple of enhancements requests submitted &lt;a href="https://designgridlayout.dev.java.net/servlets/ProjectIssues"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The main enhancement planned for 1.0 will be the &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=5"&gt;support for variable height rows&lt;/a&gt; that would get extra height during resize; this is particularly useful for rows that include components such as JList, JTable, or more generally any component wrapped in a JScrollPane.&lt;br /&gt;&lt;br /&gt;Another interesting feature I would like in 1.0 release is &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=10"&gt;components spanning several rows&lt;/a&gt;. This is a particularly useful feature and I know several users have been expecting it. The difficult part here will be to define the right API for that, in order to keep this feature easy to use, easy to visualize and safe (reducing potential errors at runtime by catching them at compile-time).&lt;br /&gt;&lt;br /&gt;Finally, 1.0 release may include some attempts at &lt;a href="https://designgridlayout.dev.java.net/issues/show_bug.cgi?id=9"&gt;right-to-left languages support&lt;/a&gt;. This will depend on several factors.&lt;br /&gt;&lt;br /&gt;In any case, if you are a DesignGridLayout user or consider it for your next Swing GUI, please take your chance and participate in the discussions on the 3 issues above, so that DesignGridLayout can keep its spirit while bringing important features common in daily GUI design work.&lt;br /&gt;&lt;br /&gt;Of course you can also suggest other enhancements and report bugs if you find any.&lt;br /&gt;&lt;br /&gt;You can find more details on DesignGridLayout &lt;a href="https://designgridlayout.dev.java.net/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Enjoy GUI design with DesignGridLayout!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-7586247026757843706?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/7586247026757843706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/09/designgridlayout-news.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7586247026757843706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/7586247026757843706'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/09/designgridlayout-news.html' title='DesignGridLayout news'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-5865653133841693485</id><published>2008-08-03T09:31:00.005+07:00</published><updated>2008-08-03T10:22:26.316+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Plugin'/><title type='text'>Dynamically loading plugins in Java (without OSGi)</title><content type='html'>In a previous &lt;a href="http://jfpoilpret.blogspot.com/2008/07/dynamically-loading-guice-modules-as.html"&gt;post&lt;/a&gt;, I have discussed a simple solution to find dynamically find Guice &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt;s located in jar files, placed in a given directory but not initially part of an application classpath.&lt;br /&gt;&lt;br /&gt;After some discussions, on the &lt;a href="http://groups.google.com/group/google-guice/browse_thread/thread/b2e6bf52905a969"&gt;Guice mailing list&lt;/a&gt;, linked to that topic, I found that there had been some similar efforts to do the same (or almost the same); in particular, Java 6 brought the &lt;span style="font-family:courier new;"&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html"&gt;java.util.ServiceLoader&lt;/a&gt;&lt;/span&gt; class that does the same, except that the dynamic jar files discovery is left to you (by creating your own &lt;span style="font-family:courier new;"&gt;ClassLoader&lt;/span&gt; to include these jars).&lt;br /&gt;&lt;br /&gt;Based on the discussions above, I have reworked my prototype to make it generic, thus independent of Guice (but usable with Gucie Modules of course).&lt;br /&gt;&lt;br /&gt;This is what I'll present in this post today. Some code (or even entire classes) from my previous post have been reused.&lt;br /&gt;&lt;br /&gt;The basic API for "service discovery" lies in the &lt;span style="font-family:courier new;"&gt;PluginDiscoveryManager&lt;/span&gt; interface:&lt;br /&gt;&lt;pre&gt;public interface PluginDiscoveryManager&lt;br /&gt;{&lt;br /&gt;    public &amp;lt;T&amp;gt; Iterable&amp;lt;T&amp;gt; getPluggedServices(Class&amp;lt;T&amp;gt; service);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Usage is quite straightforward once you got an implementation of that interface, you just need to call &lt;span style="font-family:courier new;"&gt;getPluggedServices()&lt;/span&gt; passing it the type of services you want to dynamically load:&lt;br /&gt;&lt;pre&gt;PluginDiscoveryManager manager = ...;&lt;br /&gt;for (IMyService service: manager.getPluggedServices(IMyService.class))&lt;br /&gt;{&lt;br /&gt;    // Do something with service&lt;br /&gt;    service.doSomething();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;My prototype has currently two implementations for &lt;span style="font-family:courier new;"&gt;PluginDiscoveryManager&lt;/span&gt;: &lt;span style="font-family:courier new;"&gt;ClassPathPluginDiscoveryManager&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;DirectoryPluginDiscoveryManager&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ClassPathPluginDiscoveryManager&lt;/span&gt; looks for services in jars that are already in the classpath, whereas &lt;span style="font-family:courier new;"&gt;DirectoryPluginDiscoveryManager&lt;/span&gt; will dynamically add all jars present in a given directory to the classpath before searching for services.&lt;br /&gt;&lt;br /&gt;Let's take a look at &lt;span style="font-family:courier new;"&gt;ClassPathPluginDiscoveryManager&lt;/span&gt; first (I removed all exception handling code for clarity):&lt;br /&gt;&lt;pre&gt;public class ClassPathPluginDiscoveryManager implements PluginDiscoveryManager&lt;br /&gt;{&lt;br /&gt;    static private final String MANIFEST_PATH = "META-INF/MANIFEST.MF";&lt;br /&gt;    static private final String MANIFEST_PLUGIN_ATTRIBUTE = "Plugins";&lt;br /&gt;&lt;br /&gt;    public ClassPathPluginDiscoveryManager(ClassLoader loader)&lt;br /&gt;    {&lt;br /&gt;        _loader = loader;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public &amp;lt;T&amp;gt; Iterable&amp;lt;T&amp;gt; getPluggedServices(Class&amp;lt;T&amp;gt; clazz)&lt;br /&gt;    {&lt;br /&gt;        List&amp;lt;T&amp;gt; services = new ArrayList&amp;lt;T&amp;gt;();&lt;br /&gt;        // Get all MANIFEST files in the classpath&lt;br /&gt;        Enumeration&amp;lt;URL&amp;gt; manifests = _loader.getResources(MANIFEST_PATH);&lt;br /&gt;        while (manifests.hasMoreElements())&lt;br /&gt;        {&lt;br /&gt;            addOnePluginServices(manifests.nextElement(), clazz, services);&lt;br /&gt;        }&lt;br /&gt;        return services;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private &amp;lt;T&amp;gt; void addOnePluginServices(&lt;br /&gt;        URL manifestUrl, Class&amp;lt;T&amp;gt; clazz, List&amp;lt;T&amp;gt; services)&lt;br /&gt;    {&lt;br /&gt;        InputStream input = null;&lt;br /&gt;        input = manifestUrl.openStream();&lt;br /&gt;        Manifest manifest = new Manifest(input);&lt;br /&gt;        String implementations =&lt;br /&gt;            manifest.getMainAttributes().getValue(MANIFEST_PLUGIN_ATTRIBUTE);&lt;br /&gt;        if (implementations != null)&lt;br /&gt;        {&lt;br /&gt;            for (String impl: implementations.split("[ \t]+"))&lt;br /&gt;            {&lt;br /&gt;                addOneService(impl, clazz, services);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private &amp;lt;T&amp;gt; void addOneService(&lt;br /&gt;        String implementation, Class&amp;lt;T&amp;gt; clazz, List&amp;lt;T&amp;gt; services)&lt;br /&gt;    {&lt;br /&gt;        Class&amp;lt;?&amp;gt; service = Class.forName(implementation, false, _loader);&lt;br /&gt;        // Check is service implements clazz&lt;br /&gt;        if (clazz.isAssignableFrom(service))&lt;br /&gt;        {&lt;br /&gt;            services.add(clazz.cast(service.newInstance()));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private final ClassLoader _loader;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;As in my previous prototype with Guice, service implementation classes are discovered by checking a special attribute in the MANIFEST file:&lt;br /&gt;&lt;pre&gt;Plugins: package1.MyService1Impl package2.MyService2Impl&lt;br /&gt;&lt;/pre&gt;What's new here is that you can mix different types of services in the attribute (with Guice, they all had to be Guice &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt;s). &lt;span style="font-family:courier new;"&gt;ClassPathPluginDiscoveryManager.getPluggedServices()&lt;/span&gt; will retain only implementations for the required service class.&lt;br /&gt;&lt;br /&gt;Now &lt;span style="font-family:courier new;"&gt;DirectoryPluginDiscoveryManager&lt;/span&gt; is simply based on &lt;span style="font-family:courier new;"&gt;ClassPathPluginDiscoveryManager&lt;/span&gt; but creates a special &lt;span style="font-family:courier new;"&gt;ClassLoader&lt;/span&gt; to dynamically inspect the required directories:&lt;br /&gt;&lt;pre&gt;public class DirectoryPluginDiscoveryManager extends ClassPathPluginDiscoveryManager&lt;br /&gt;{&lt;br /&gt;    public DirectoryPluginDiscoveryManager(&lt;br /&gt;        boolean includeSubDirs, File... directories)&lt;br /&gt;    {&lt;br /&gt;        this(Thread.currentThread().getContextClassLoader(), includeSubDirs,&lt;br /&gt;            directories);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public DirectoryPluginDiscoveryManager(&lt;br /&gt;        ClassLoader parent, boolean includeSubDirs, File... directories)&lt;br /&gt;    {&lt;br /&gt;        super(ClassLoaderHelper.buildClassLoader(includeSubDirs, parent, directories));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;All &lt;span style="font-family:courier new;"&gt;ClassLoader&lt;/span&gt; support is provided by the &lt;span style="font-family:courier new;"&gt;ClassLoaderHelper&lt;/span&gt; class, already presented in my previous post.&lt;br /&gt;&lt;br /&gt;The main advantage of the solution presented here is its simplicity: &lt;span style="font-family:courier new;"&gt;PluginDiscoveryManager&lt;/span&gt; implementations are not complex, writing a supported plugin is very simple, just a few lines in your ant or maven configuration file, e.g.&lt;br /&gt;&lt;pre&gt;&amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;        ...&lt;br /&gt;        &amp;lt;plugin&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;maven-jar-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;configuration&amp;gt;&lt;br /&gt;                &amp;lt;archive&amp;gt;&lt;br /&gt;                    &amp;lt;manifestEntries&amp;gt;&lt;br /&gt;                        &amp;lt;Plugins&amp;gt;package1.MyService1Impl package2.MyService2Impl&amp;lt;/Plugins&amp;gt;&lt;br /&gt;                    &amp;lt;/manifestEntries&amp;gt;&lt;br /&gt;                &amp;lt;/archive&amp;gt;&lt;br /&gt;            &amp;lt;/configuration&amp;gt;&lt;br /&gt;        &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt;&lt;br /&gt;&amp;lt;/build&amp;gt;&lt;br /&gt;&lt;/pre&gt;in the maven pom.xml of one actual plugin project.&lt;br /&gt;&lt;br /&gt;Additionally, this solution works with Java 5 or higher, no need to wait for JSR-277!&lt;br /&gt;&lt;br /&gt;You can find the full code of this proto, along with javadoc, tests and maven configuration in this &lt;a href="http://jfpoilpret.googlepages.com/dynamic-plugins.zip"&gt;zip archive&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-5865653133841693485?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/5865653133841693485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/08/dynamically-loading-plugins-in-java.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5865653133841693485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5865653133841693485'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/08/dynamically-loading-plugins-in-java.html' title='Dynamically loading plugins in Java (without OSGi)'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-966189175302960763</id><published>2008-07-31T19:04:00.012+07:00</published><updated>2008-07-31T20:29:59.187+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='Guice'/><title type='text'>Dynamically loading Guice modules as external plugins</title><content type='html'>Hi,&lt;br /&gt;&lt;br /&gt;today I will present some experiments I am currently working on with Guice.&lt;br /&gt;&lt;br /&gt;Nowadays, it is more and more common to see applications that users can customize simply by dropping "plugins" in some defined directory. A well-known example is Eclipse, but there are many such examples.&lt;br /&gt;&lt;br /&gt;One way to realize this is to use an "&lt;strong&gt;OSGi&lt;/strong&gt; container" and write plugins that obey OSGi specifications. But OSGi is quite a complex stuff (in particular if you intend to write an OSGi container).&lt;br /&gt;&lt;br /&gt;While working with Guice, I felt the need to have the possibility to plugin new functionality to a Guice-based application by dropping jars containing classes implementing the Guice &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt; interface. But I did not want to depend on OSGi for this -quite simple- stuff.&lt;br /&gt;&lt;br /&gt;So I have started some work on my own. The main problem for this kind of system is to &lt;em&gt;discover&lt;/em&gt; all &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt;s that can be used to create a Guice &lt;span style="font-family:courier new;"&gt;Injector&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;First of all, in order to permit several implementations, I have defined an interface for &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt; discovery:&lt;br /&gt;&lt;pre&gt;public interface ModulesDiscoveryManager&lt;br /&gt;{&lt;br /&gt;    public Iterable&amp;lt;Module&amp;gt; getModules();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;In this post, I will show two implementations of this interface.&lt;br /&gt;&lt;br /&gt;The first implementation, &lt;span style="font-family:courier new;"&gt;ClassPathModulesDiscoveryManager&lt;/span&gt;, is quite straight-forward but does not fulfill completely the initial requirements (but we will reuse this class for the other implementation class).&lt;br /&gt;&lt;pre&gt;public class ClassPathModulesDiscoveryManager implements ModulesDiscoveryManager&lt;br /&gt;{&lt;br /&gt;    static private final String MANIFEST_PATH = "META-INF/MANIFEST.MF";&lt;br /&gt;    static private final String MANIFEST_GUICE_NAME = "Guice-Modules";&lt;br /&gt;&lt;br /&gt;    public ClassPathModulesDiscoveryManager()&lt;br /&gt;    {&lt;br /&gt;        this(Thread.currentThread().getContextClassLoader());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ClassPathModulesDiscoveryManager(ClassLoader loader)&lt;br /&gt;    {&lt;br /&gt;        _logger.log(Level.INFO, "Loading all dynamic Guice Module");&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            // Get all MANIFEST files in the classpath&lt;br /&gt;            Enumeration&amp;lt;URL&amp;gt; manifests = loader.getResources(MANIFEST_PATH);&lt;br /&gt;            while (manifests.hasMoreElements())&lt;br /&gt;            {&lt;br /&gt;                addModule(loader, manifests.nextElement());&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        catch (IOException e)&lt;br /&gt;        {&lt;br /&gt;            // Should not happen&lt;br /&gt;            _logger.log(Level.SEVERE, "Could not add all dynamic Modules", e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void addModule(ClassLoader loader, URL manifestUrl)&lt;br /&gt;    {&lt;br /&gt;        InputStream input = null;&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            input = manifestUrl.openStream();&lt;br /&gt;            Manifest manifest = new Manifest(input);&lt;br /&gt;            String modules = manifest.getMainAttributes().getValue(MANIFEST_GUICE_NAME);&lt;br /&gt;            if (modules != null)&lt;br /&gt;            {&lt;br /&gt;                for (String module: modules.split("[ \t]+"))&lt;br /&gt;                {&lt;br /&gt;                    addModule(loader, module);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        catch (IOException e)&lt;br /&gt;        {&lt;br /&gt;            _logger.log(Level.SEVERE, "addModule problem with URL: " + manifestUrl, e);&lt;br /&gt;        }&lt;br /&gt;        finally&lt;br /&gt;        {&lt;br /&gt;            if (input != null)&lt;br /&gt;            {&lt;br /&gt;                close(input);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void addModule(ClassLoader loader, String module)&lt;br /&gt;    {&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            Class&amp;lt;?&amp;gt; clazz = Class.forName(module, true, loader);&lt;br /&gt;            _modules.add((Module) clazz.newInstance());&lt;br /&gt;            _logger.log(Level.INFO, "Guice Module %s dynamically added.", module);&lt;br /&gt;        }&lt;br /&gt;        catch (Exception e)&lt;br /&gt;        {&lt;br /&gt;            _logger.log(Level.SEVERE, "addModule problem with: " + module, e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Iterable&amp;lt;Module&amp;gt; getModules()&lt;br /&gt;    {&lt;br /&gt;        return _modules;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static final private Logger _logger =&lt;br /&gt;        Logger.getLogger(ClassPathModulesDiscoveryManager.class.getName());&lt;br /&gt;    private final List&amp;lt;Module&amp;gt; _modules = new ArrayList&amp;lt;Module&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Note that the snippet above does not include the obvious &lt;span style="font-family:courier new;"&gt;close()&lt;/span&gt; method.&lt;br /&gt;The main idea for discovering classes implementing &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt; is to require some little help from the plugins developers: add a special attribute in the &lt;em&gt;"META-INF/MANIFEST.MF"&lt;/em&gt; file of every jar to define the &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt; class names (full name, including package) included in that jar:&lt;br /&gt;&lt;pre&gt;Guice-Modules: package1.MyModule1 package2.MyModule2&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-family:courier new;"&gt;ClassPathModulesDiscoveryManager&lt;/span&gt; implementation is quite straightforward, given a &lt;span style="font-family:courier new;"&gt;ClassLoader&lt;/span&gt;, the constructor searches for all &lt;em&gt;META-INF/MANIFEST.MF&lt;/em&gt; resources, then looks for a &lt;span style="font-family:courier new;"&gt;"Guice-Modules"&lt;/span&gt; attribute, extracts each &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt; class (separated by spaces) and finally loads each class with the provided &lt;span style="font-family:courier new;"&gt;ClassLoader&lt;/span&gt; and instantiates it (it must have a public no-arg constructor).&lt;br /&gt;&lt;br /&gt;Now comes the second implementation of the &lt;span style="font-family:courier new;"&gt;ClassPathModulesDiscoveryManager&lt;/span&gt; interface, &lt;span style="font-family:courier new;"&gt;DirectoryModulesDiscoveryManager&lt;/span&gt;; this one is a subclass of &lt;span style="font-family:courier new;"&gt;ClassPathModulesDiscoveryManager&lt;/span&gt;.&lt;br /&gt;&lt;pre&gt;public class DirectoryModulesDiscoveryManager extends ClassPathModulesDiscoveryManager&lt;br /&gt;{&lt;br /&gt;    public DirectoryModulesDiscoveryManager(&lt;br /&gt;        boolean includeSubDirs, File... directories)&lt;br /&gt;    {&lt;br /&gt;        this(Thread.currentThread().getContextClassLoader(), includeSubDirs,&lt;br /&gt;            directories);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public DirectoryModulesDiscoveryManager(&lt;br /&gt;        ClassLoader parent, boolean includeSubDirs, File... directories)&lt;br /&gt;    {&lt;br /&gt;        super(ClassLoaderHelper.buildClassLoader(&lt;br /&gt;            Arrays.asList(directories), includeSubDirs, parent));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This class is quite simple, but most of its work is performed by another helper class. That helper is actually in charge of creating a new ClassLoader that will add all jars found in a given set of directories to the classpath:&lt;br /&gt;&lt;pre&gt;final class ClassLoaderHelper&lt;br /&gt;{&lt;br /&gt;    static ClassLoader buildClassLoader(&lt;br /&gt;        List&amp;lt;File&amp;gt; directories, boolean includeSubDirs)&lt;br /&gt;    {&lt;br /&gt;        return buildClassLoader(&lt;br /&gt;            directories, includeSubDirs, Thread.currentThread().getContextClassLoader());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static ClassLoader buildClassLoader(&lt;br /&gt;        List&amp;lt;File&amp;gt; directories, boolean includeSubDirs, ClassLoader parent)&lt;br /&gt;    {&lt;br /&gt;        List&amp;lt;URL&amp;gt; allJars = new ArrayList&amp;lt;URL&amp;gt;();&lt;br /&gt;        // Find all Jars in each directory&lt;br /&gt;        for (File dir: directories)&lt;br /&gt;        {&lt;br /&gt;            fillJarsList(allJars, dir, includeSubDirs);&lt;br /&gt;        }&lt;br /&gt;        return new URLClassLoader(allJars.toArray(new URL[allJars.size()]), parent);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static private void fillJarsList(List&amp;lt;URL&amp;gt; jars, File dir, boolean includeSubDirs)&lt;br /&gt;    {&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            for (File jar: dir.listFiles(_jarsFilter))&lt;br /&gt;            {&lt;br /&gt;                jars.add(jar.toURI().toURL());&lt;br /&gt;            }&lt;br /&gt;     &lt;br /&gt;            if (includeSubDirs)&lt;br /&gt;            {&lt;br /&gt;                for (File subdir: dir.listFiles(_dirsFilter))&lt;br /&gt;                {&lt;br /&gt;                    fillJarsList(jars, subdir, true);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        catch (Exception e)&lt;br /&gt;        {&lt;br /&gt;            // Should not happen&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static final private FileFilter _jarsFilter = new FileFilter() {...};&lt;br /&gt;    static final private FileFilter _dirsFilter = new FileFilter() {...};&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;The snippet above doesn't show the obvious &lt;span style="font-family:courier new;"&gt;FileFilter&lt;/span&gt;s used to traverse directories and search for jar files.&lt;br /&gt;&lt;br /&gt;The main bits are in:&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;fillJarsList()&lt;/span&gt;: add all jar files to a List (after converting these files into URLs)&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;buildClassLoader()&lt;/span&gt;: &lt;span style="font-family:courier new;"&gt;new URLClassLoader(allJars.toArray(new URL[allJars.size()]), parent);&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;That's all there is to it!&lt;br /&gt;&lt;br /&gt;Now you can build Guice-based applications with Guice-friendly plugins! Just do that in your main entry point method:&lt;br /&gt;&lt;pre&gt;ModulesDiscoveryManager manager = new DirectoryModulesDiscoveryManager(&lt;br /&gt;    true, new File("SomePluginDirectory"));&lt;br /&gt;Guice.createInjector(manager.getModules());&lt;br /&gt;&lt;/pre&gt;You can find a complete maven project with tests in &lt;a href="http://jfpoilpret.googlepages.com/guice-plugins.zip"&gt;this zip&lt;/a&gt;. The tests need 4 simple plugins that are built by 4 sub-projects.&lt;br /&gt;To use the source code, just unzip the file somewhere on your disk, it will create a "plugins" directory that contains a maven pom.xml. Type mvn install there and it will create all plugins used by the tests, then the main "manager" project that contains the source code presented in this post.&lt;br /&gt;&lt;br /&gt;Eventually, this code may be part of my future Guice-GUI framework which shall be released by the end of August.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-966189175302960763?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/966189175302960763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/07/dynamically-loading-guice-modules-as.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/966189175302960763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/966189175302960763'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/07/dynamically-loading-guice-modules-as.html' title='Dynamically loading Guice modules as external plugins'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-4866342063813042879</id><published>2008-07-27T20:49:00.004+07:00</published><updated>2008-07-27T21:05:28.917+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><category scheme='http://www.blogger.com/atom/ns#' term='DesignGridLayout'/><category scheme='http://www.blogger.com/atom/ns#' term='Layout'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>New OSS project responsibility!</title><content type='html'>About one month ago, I have been assigned owner of the &lt;a href="http://designgridlayout.dev.java.net/"&gt;DesignGridLayout &lt;/a&gt;project.&lt;br /&gt;I was very happy for that because:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I like this project a lot (since the first time I knew it, in November 2007)&lt;/li&gt;&lt;li&gt;But it seemed to have been dormant for more than one year&lt;/li&gt;&lt;/ol&gt;Most of the few issues open on the project were posted by me, hence I had to fix them by myself in the past.&lt;br /&gt;&lt;br /&gt;Recently, I thought about completely refactoring DesignGridLayout source code, which I did, in order to improve its API furthermore and make it easier to enhance later on.&lt;br /&gt;&lt;br /&gt;Since I have several improvement ideas, I have then contacted the curren project owner to ask him if he was interested in my work. His answer was that unfortunately he is much too busy to keep the project living and thus he suggested that I could become owner of the project and keep it alive! That's how it all happened!&lt;br /&gt;&lt;br /&gt;From then on, I have started to take hold of the project and suggest important changes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;more open license (GPL currently)&lt;/li&gt;&lt;li&gt;upload to maven2 repository&lt;/li&gt;&lt;li&gt;switching from CVS to Subversion&lt;/li&gt;&lt;li&gt;API improvements&lt;/li&gt;&lt;/ul&gt;I truly believe that DesignGridLayout is a revolutionary &lt;span style="font-family:courier new;"&gt;LayoutManager&lt;/span&gt; for Swing applications but unfortunately, there was not much advertising about it so far.&lt;br /&gt;&lt;br /&gt;If you are fed up of standard Swing layouts (&lt;span style="font-family:courier new;"&gt;GridBagLayout&lt;/span&gt; in particular;-)) and you find 3rd party OSS layouts a bit steep to learn, then you should definately take a look at &lt;a href="http://designgridlayout.dev.java.net/"&gt;DesignGridLayout&lt;/a&gt;, it is worth your (little) time!&lt;br /&gt;&lt;br /&gt;I'll blog more about DesignGridLayout soon after I have released a new version including the whole refactored source code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-4866342063813042879?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/4866342063813042879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/07/new-oss-project-responsibility.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/4866342063813042879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/4866342063813042879'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/07/new-oss-project-responsibility.html' title='New OSS project responsibility!'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9018398.post-5765135642573942661</id><published>2008-07-27T14:24:00.013+07:00</published><updated>2008-07-27T21:36:28.739+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Guice'/><category scheme='http://www.blogger.com/atom/ns#' term='hooks'/><title type='text'>How to intercept Guice instance creation?</title><content type='html'>In this first post, I'll talk about some of my experiments with &lt;a href="http://code.google.com/p/google-guice/"&gt;Google Guice &lt;/a&gt;Dependency Injection library.&lt;br /&gt;&lt;br /&gt;I am using Guice 1.0 although 2.0 is planned for release this summer (but I prefer to wait and see...) Anyway, the problem I'm going to talk about seems not to be addressed in the future Guice 2.0 either (although the solution might be simpler later in 2.0, we'll see).&lt;br /&gt;&lt;br /&gt;Guice is a very good library for Dependency Injection, it is heavily depending on Java 5 Generics thus ensuring very high confidence in your code robustness at compile-time. It is also using annotations a lot.&lt;br /&gt;&lt;br /&gt;Guice API is quite simple for the end user, however it may be considered &lt;strong&gt;too&lt;/strong&gt; simple, considering a few missing features that look fundamental.&lt;br /&gt;&lt;br /&gt;As an example, I have a use case where I need to process annotations (not Guice annotations, but other unrelated annotations) to any class instance created and injected by Guice. How can I do that? Guice 1.0 (and 2.0 as far as I understand) provides no "hook" system that would enable me to be notified whenever a new instance has been created and injected by Guice Injector. But that's exactly what one needs to be able to add the necessary annotation processing code for all new instances created by Guice!&lt;br /&gt;&lt;br /&gt;So how can we do that?&lt;br /&gt;&lt;br /&gt;After several experiments, I think I have found one way, not perfect (I'll explain why later), but still acceptable, that works fine with Guice 1.0 without any hack.&lt;br /&gt;&lt;br /&gt;I call this way "&lt;strong&gt;Guice creation hooks through Scope interception&lt;/strong&gt;". Wait a minute! Guice does not provide any way to intercept &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; methods! Correct, in Guice, &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt;s are not first-class citizen and cannot be injected, intercepted... That seems quite natural however (just imagine a &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; that can be scoped!).&lt;br /&gt;&lt;br /&gt;So how do we intercept &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; methods then? Well, there are not too many possibilities: you have to create your own &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt;s, and have them delegate to real &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt;s (like &lt;span style="font-family:courier new;"&gt;Scopes.SINGLETON&lt;/span&gt; which is one of the few standard Scopes that Guice brings out of the box).&lt;br /&gt;Creating a new &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; means creating a class implementing the &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; interface, and defining an Annotation for it. Since we are creating Scopes that wrap other Scopes, we'll have to define one new Annotation for each Scope we want to wrap (that's the most boring part).&lt;br /&gt;&lt;br /&gt;In my example, I'll define only 2 annotations used to define 2 scopes that respectively wrap Singleton and Guice default scope (which has no specific name):&lt;br /&gt;&lt;pre&gt;@Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;@Target(ElementType.TYPE)&lt;br /&gt;@ScopeAnnotation&lt;br /&gt;public @interface HookedSingleton&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And exactly the same for the "no scope" annotation:&lt;br /&gt;&lt;pre&gt;@Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;@Target(ElementType.TYPE)&lt;br /&gt;@ScopeAnnotation&lt;br /&gt;public @interface HookedDefaultScope&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Unfortunately I couldn't find better names for these annotations:-(&lt;br /&gt;&lt;br /&gt;Now let's see the implementation of the class implementing &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;public class HookedScope implements Scope&lt;br /&gt;{&lt;br /&gt;    private final Scope _wrappedScope;&lt;br /&gt;    private final List&amp;lt;CreationHook&amp;gt; _hooks = new ArrayList&amp;lt;CreationHook&amp;gt;();&lt;br /&gt;&lt;br /&gt;    public HookedScope(Scope scope)&lt;br /&gt;    {&lt;br /&gt;        _wrappedScope = scope;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void addHook(CreationHook hook)&lt;br /&gt;    {&lt;br /&gt;        _hooks.add(hook);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public &amp;lt;T&amp;gt; Provider&amp;lt;T&amp;gt; scope(Key&amp;lt;T&amp;gt; key, final Provider&amp;lt;T&amp;gt; creator)&lt;br /&gt;    {&lt;br /&gt;        Provider&amp;lt;T&amp;gt; hookedCreator = new Provider&amp;lt;T&amp;gt;()&lt;br /&gt;        {&lt;br /&gt;            public T get()&lt;br /&gt;            {&lt;br /&gt;                // Actually create a new T instance&lt;br /&gt;                T instance = creator.get();&lt;br /&gt;                // Add processing to newly created instance&lt;br /&gt;                for (CreationHook hook: _hooks)&lt;br /&gt;                {&lt;br /&gt;                    hook.process(instance);&lt;br /&gt;                }&lt;br /&gt;                return instance;&lt;br /&gt;            }&lt;br /&gt;        };&lt;br /&gt;        // Make sure that we are called back whenever an instance is actually&lt;br /&gt;        // created by unscoped&lt;br /&gt;        if (_wrappedScope != null)&lt;br /&gt;        {&lt;br /&gt;            return _wrappedScope.scope(key, hookedCreator);&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;            return hookedCreator;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This class has to be instantiated once for every scope used in the application (you want to hook &lt;strong&gt;ALL&lt;/strong&gt; objects instantiation, don't you?)&lt;br /&gt;&lt;br /&gt;This class works as follows:&lt;br /&gt;First it stores the actual &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; that it is wrapping.&lt;br /&gt;Then, when its &lt;span style="font-family:courier new;"&gt;scope()&lt;/span&gt; method is called, it is passed (&lt;span style="font-family:courier new;"&gt;creator&lt;/span&gt;, 2nd argument) a special &lt;span style="font-family:courier new;"&gt;Provider&lt;t&gt;&lt;/span&gt; that is the &lt;strong&gt;one&lt;/strong&gt; &lt;span style="font-family:courier new;"&gt;Provider&lt;/span&gt; that unconditionnally instantiates a &lt;span style="font-family:courier new;"&gt;T&lt;/span&gt;. Thus if we just decorate &lt;span style="font-family:courier new;"&gt;creator&lt;/span&gt;, and pass it along to the real &lt;span style="font-family:courier new;"&gt;Scope&lt;/span&gt; that we wrap, the decorated &lt;span style="font-family:courier new;"&gt;Provider &lt;/span&gt;will be called every time a new creation is about to happen. Our decorated &lt;span style="font-family:courier new;"&gt;Provider&lt;/span&gt; (named &lt;span style="font-family:courier new;"&gt;hookedCreator&lt;/span&gt;) first simply calls the real Guice &lt;span style="font-family:courier new;"&gt;creator&lt;/span&gt;, then it passes the newly created instance to any hook that is registered to it (through its &lt;span style="font-family:courier new;"&gt;addHook&lt;/span&gt; method).&lt;br /&gt;&lt;br /&gt;Hooks just have to implement the &lt;span style="font-family:courier new;"&gt;CreationHook&lt;/span&gt; interface below:&lt;br /&gt;&lt;pre&gt;public interface CreationHook&lt;br /&gt;{&lt;br /&gt;    public &amp;lt;T&amp;gt; void process(T instance);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now the next step is to actually instantiate our &lt;span style="font-family:courier new;"&gt;HookedScope&lt;/span&gt;s and bind them to their&lt;br /&gt;respective annotations, this is done in a Guice &lt;span style="font-family:courier new;"&gt;Module&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;public class ScopeModule extends AbstractModule&lt;br /&gt;{&lt;br /&gt;    static HookedScope SINGLETON = new HookedScope(Scopes.SINGLETON);&lt;br /&gt;    static HookedScope NO_SCOPE = new HookedScope(null);&lt;br /&gt;&lt;br /&gt;    @Override protected void configure()&lt;br /&gt;    {&lt;br /&gt;        bindScope(HookedSingleton.class, SINGLETON);&lt;br /&gt;        bindScope(HookedDefaultScope.class, NO_SCOPE);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And you pass this module when you create the Guice Injector:&lt;br /&gt;&lt;pre&gt;    Injector injector = Guice.createInjector(new ScopeModule(), ...);&lt;br /&gt;&lt;/pre&gt;Note the static &lt;span style="font-family:courier new;"&gt;SINGLETON&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;NO_SCOPE&lt;/span&gt; defined above, they will be needed so that you can&lt;br /&gt;add your hooks when you need:&lt;br /&gt;&lt;pre&gt;    CreationHook hook = ...;&lt;br /&gt;    ScopeModule.SINGLETON.addHook(hook);&lt;br /&gt;    ScopeModule.NO_SCOPE.addHook(hook);&lt;br /&gt;&lt;/pre&gt;As you can see, this leads to a new problem: you will have to add your hooks to each individual&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;HookedScope&lt;/span&gt; that you have created; in the example above that's only 2 scopes, but you may have more in your application!&lt;br /&gt;&lt;br /&gt;Thus, a better way is to implement the whole logic of creating the &lt;span style="font-family:courier new;"&gt;HookedScope&lt;/span&gt;s and adding hooks in one single helper class:&lt;br /&gt;&lt;pre&gt;final public class GuiceHookHelper&lt;br /&gt;{&lt;br /&gt;    static private final Map&amp;lt;Scope, Scope&amp;gt; _allScopes =&lt;br /&gt;        new HashMap&amp;lt;Scope, Scope&amp;gt;();&lt;br /&gt;    static private final List&amp;lt;CreationHook&amp;gt; _hooks =&lt;br /&gt;        new ArrayList&amp;lt;CreationHook&amp;gt;();&lt;br /&gt;&lt;br /&gt;    // Use the following improved scopes in bind()...in(scope) instead&lt;br /&gt;    // of Scoped.SINGLETON or no scope at all.&lt;br /&gt;    static final public Scope SINGLETON = hook(Scopes.SINGLETON);&lt;br /&gt;    static final public Scope NO_SCOPE = hook(null);&lt;br /&gt;&lt;br /&gt;    static public Scope hook(Scope wrappedScope)&lt;br /&gt;    {&lt;br /&gt;        Scope result = _allScopes.get(wrappedScope);&lt;br /&gt;        if (result == null)&lt;br /&gt;        {&lt;br /&gt;            result = new HookedScope(wrappedScope);&lt;br /&gt;            _allScopes.put(wrappedScope, result);&lt;br /&gt;        }&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static public void addHook(CreationHook hook)&lt;br /&gt;    {&lt;br /&gt;        _hooks.add(hook);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static public void removeHook(CreationHook hook)&lt;br /&gt;    {&lt;br /&gt;        _hooks.remove(hook);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static private class HookedScope implements Scope&lt;br /&gt;    {&lt;br /&gt;        private final Scope _wrappedScope;&lt;br /&gt;&lt;br /&gt;        private HookedScope(Scope scope)&lt;br /&gt;        {&lt;br /&gt;            _wrappedScope = scope;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public &amp;lt;T&amp;gt; Provider&amp;lt;T&amp;gt; scope(Key&amp;lt;T&amp;gt; key, final Provider&amp;lt;T&amp;gt; creator)&lt;br /&gt;        {&lt;br /&gt;            Provider&amp;lt;T&amp;gt; hookedCreator = new Provider&amp;lt;T&amp;gt;()&lt;br /&gt;            {&lt;br /&gt;                public T get()&lt;br /&gt;                {&lt;br /&gt;                    // Actually create a new T instance&lt;br /&gt;                    T instance = creator.get();&lt;br /&gt;                    // Add processing to newly created instance&lt;br /&gt;                    for (CreationHook hook: _hooks)&lt;br /&gt;                    {&lt;br /&gt;                        hook.process(instance);&lt;br /&gt;                    }&lt;br /&gt;                    return instance;&lt;br /&gt;                }&lt;br /&gt;            };&lt;br /&gt;            // Make sure that we are called back whenever an instance is actually&lt;br /&gt;            // created by unscoped&lt;br /&gt;            if (_wrappedScope != null)&lt;br /&gt;            {&lt;br /&gt;                return _wrappedScope.scope(key, hookedCreator);&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                return hookedCreator;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now this class centralizes all HookedScopes and all hooks, hiding all internals from the end user.&lt;br /&gt;With this class, it is now easy to wrap other scopes and add hooks.&lt;br /&gt;&lt;br /&gt;The main drawback of this approach is that you cannot use standard Guice scope annotations (&lt;span style="font-family:courier new;"&gt;@Singleton&lt;/span&gt; or no annotation at all) for scoping your classes. You have to use new annotations, even for the default scope! Compared with what it brings, I think this drawback is not that big.&lt;br /&gt;&lt;br /&gt;These classes will be part of my future &lt;strong&gt;&lt;a href="https://sourceforge.net/projects/guice-gui"&gt;Guice-GUI &lt;/a&gt;&lt;/strong&gt;framework (to be released this summer on SourceForge) where adding annotation processing to Guice instantiated classes makes sense when you want to use &lt;a href="https://eventbus.dev.java.net/"&gt;EventBus &lt;/a&gt;with its annotations for instance (common in GUI development).&lt;br /&gt;&lt;br /&gt;For the time being, you can find the complete source code for the experiments presented in this post, a maven pom.xml and eclipse project settings &lt;a href="http://jfpoilpret.googlepages.com/guice-creation-hooks.zip"&gt;here&lt;/a&gt;. Just unzip it and either import it into eclipse or install it with maven. The project includes a couple of unit tests (with TestNG).&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9018398-5765135642573942661?l=jfpoilpret.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jfpoilpret.blogspot.com/feeds/5765135642573942661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://jfpoilpret.blogspot.com/2008/07/how-to-intercept-guice-instance.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5765135642573942661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9018398/posts/default/5765135642573942661'/><link rel='alternate' type='text/html' href='http://jfpoilpret.blogspot.com/2008/07/how-to-intercept-guice-instance.html' title='How to intercept Guice instance creation?'/><author><name>Jean-Francois Poilpret</name><uri>http://www.blogger.com/profile/00898429429517716882</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://bp1.blogger.com/_qRq1w21uy-s/SIvvssUa1sI/AAAAAAAAAAM/mrcH30z4A9c/S220/jfpoilpret.jpg'/></author><thr:total>2</thr:total></entry></feed>
