David Botterill's Weblog

« Previous month (Apr 2007) | Main | Next month (Jun 2007) »

20070627 Wednesday June 27, 2007

NetBeans Plugin Portal Source Code Open Sourced

For those of you who haven't found the code yet, I thought I would officially announce that I've published the source code for the NetBeans Plugin Portal on netbeans.org.  The URL to browse the source is http://www.netbeans.org/source/browse/pluginportal/.  So now you can see all the good, the bad, and the ugly code that went into making the Plugin Portal.  I'll continue to blog in detail about the experience but if you can't wait and you need to see how something was done, please go look at the source code.  The code is under a CDDL license if you decide to use any of it wholesale.  If you see any blatant errors in the code or better ways of doing things, please open an Issue.

Posted by David Botterill ( Jun 27 2007, 01:26:18 PM MDT ) Permalink Comments [0] del.icio.us | digg | technorati

20070622 Friday June 22, 2007

NetBeans Visual Web Pack - Real World Apps Tip #3 - Tag Clouds

Finally I've found the time to show you how to do tag clouds in NetBeans 5.5 with Visual Web Pack.  In my last blog, I showed you how to create dynamic Hyperlinks.  In the blog before that, I discussed how to do dynamic content.  Both of those topics provide necessary background information for creating a tag cloud.

First, let me provide a little background on how I tackled creating a tag cloud.  Since I really didn't know what a tag cloud was, I started with the Wikipedia's definition.  Next, since my task was not to be an expert on tag clouds but to implement one on the web site, I simply needed an algorithm to get me started.  I found an excellent white paper by Kevin Hoffman called, "In Search Of...The Perfect Tag Cloud".   Based on the Plugin Portal needs and Kevin's explanation, I decided to use the "Linear Distribution Algorithm".

Plugin Portal Details


The goal is to basically have a set of words that are displayed in different font sizes based on how many "things" there are for that word.  With the Plugin Portal, the words represent categories.  The bolder, bigger font category names are the ones with more entries in the category.  The sample project I'll include will include hard coded data but I thought I would take a moment to talk about the query behind the Plugin Portal that gave me the results I needed.  In the case of the Plugin Portal, I needed to define a type that contained a category name and a category count.  I created a class called "CategoryCount".  Here's the code including the query that creates the "CategoryCount" instances.

        String select = "select distinct o.categoryname, cc.cat_count from othercategoryimpl o," +
" (select distinct categoryname,count(categoryname) as cat_count from othercategoryimpl cc group by categoryname) as cc" +
" where o.categoryname=cc.categoryname order by o.categoryname";
Query query = em.createNativeQuery(select);
List resultList = query.getResultList();
Iterator resultIterator = resultList.iterator();
Vector currentResult = null;
while(resultIterator.hasNext()) {
currentResult = (Vector)resultIterator.next();
String categoryName = (String)currentResult.get(0); //category name.
Integer categoryCount = (Integer)currentResult.get(1); //count
CategoryCount currentRecord = new CategoryCount(categoryName, categoryCount.intValue());
newCache.add(currentRecord);
}

You can see from this SQL query that I really have to do two passes of the data.  The "from" target:

(select distinct categoryname,count(categoryname) as cat_count from othercategoryimpl cc group by categoryname) as cc
gets the actual count for each category.  Believe me this query took a while to get right. :)  I don't claim to be a SQL expert so if someone knows of an easier method, please let me know.

Creating the "Comparator"

Part of the goal also is to have the words sorted alphabetically so I chose to use "Arrays.sort".  To use this method, we need to create a specialized "Comparator" that knows how to compare our "Count" type.  For my simplified example, I will be creating "CategoryCount" as the "Count" type.  Here's the code.

public class CategoryCount {

/** Creates a new instance of CategoryCount */
public CategoryCount() {
}

public CategoryCount(String inName, int inCount) {
setName(inName);
setCount(inCount);
}

/**
* Holds value of property name.
*/
private String name;

/**
* Getter for property name.
* @return Value of property name.
*/
public String getName() {
return this.name;
}

/**
* Setter for property name.
* @param name New value of property name.
*/
public void setName(String name) {
this.name = name;
}

/**
* Holds value of property count.
*/
private int count;

/**
* Getter for property count.
* @return Value of property count.
*/
public int getCount() {
return this.count;
}

/**
* Setter for property count.
* @param count New value of property count.
*/
public void setCount(int count) {
this.count = count;
}

}

Also we need a Comparator to sort this type.  Here's the "CountComparator".

import java.util.Comparator;


public class CountComparator implements Comparator {

/** Creates a new instance of CountComparator */
public CountComparator() {
}
/**
* This method is used to compare o1 and o2.
* @param o1 The first object to compare to o2
* @param o2 The second object to compare to o1
* @return a negative integer, zero, or a positive integer if o1
* is less than, equal to, or greater than o2
*/
public int compare(Object o1, Object o2) {

String o1categoryname = ((CategoryCount)o1).getName();
String o2categoryname = ((CategoryCount)o2).getName();
return o1categoryname.compareToIgnoreCase(o2categoryname);

}
}

The Linear Distribution Algorithm Details

The idea behind this algorithm is to figure out a range for a set of "Buckets" then distribute your items in the buckets according to where they fit into the range.  The first step is to decide on the number of buckets to use.  In a tag cloud you'll be deciding how many font size ranges you want.  For the Plugin Portal, we chose six buckets.  So to figure out the range, you need to take the max count of the items - the min count of the items and divide by the number of buckets.  This gives you the range to use for each bucket.  Here's the code I'll use in my sample to figure out the range.

    private long getCloudRange(int min, int max) {
/**
* For this tag cloud we will use the linear distribution algorithm with six different "buckets".
* The algorithm is:
* category weight = max number/category - min number/category
* range = category weight/number of buckets (6).
*
*/
long weight = 0;
long range = 0;

weight = max - min;
if(weight < 6) {
/**
* We don't have a very big range of numbers in categories so we need to increase the
* weight so the range will be bigger.
*/
range = 1;
} else {
range = weight/6;
}


return range;
}
The other key piece of the implementation is to assign a font based on which bucket the item fits into.  Here's my sample method to do that.

    private String getFont(CategoryCount inCount,long bucketRange) {
String fontsize1="font-size: 10px; font-weight: normal;";
String fontsize2="font-size: 11px; font-weight: normal;";
String fontsize3="font-size: 12px; font-weight: normal;";
String fontsize4="font-size: 13px; font-weight: normal;";
String fontsize5="font-size: 14px; font-weight: normal;";
String fontsize6="font-size: 15px; font-weight: normal;";

/**
* Determine which of the buckets the count falls into.
*/
if(inCount.getCount() >= 0 && inCount.getCount() <= bucketRange) {
return fontsize1;
} else if(inCount.getCount() >= (bucketRange*1) +1 && inCount.getCount() <= (bucketRange*1)+bucketRange) {
return fontsize2;
} else if(inCount.getCount() >= (bucketRange*2) +1 && inCount.getCount() <= (bucketRange*2)+bucketRange) {
return fontsize3;
} else if(inCount.getCount() >= (bucketRange*3) +1 && inCount.getCount() <= (bucketRange*3)+bucketRange) {
return fontsize4;
} else if(inCount.getCount() >= (bucketRange*4) +1 && inCount.getCount() <= (bucketRange*4)+bucketRange) {
return fontsize5;
} else {
return fontsize6;
}
}

Leveraging our dynamic Hyperlink experience, we will need a block of code like this create the actual Tag Cloud.

        /**
* Sort the CategoryCounts
*/
Arrays.sort(counts,new CountComparator());

/**
* Get the range to use for the tag cloud.
*/
long range = getCloudRange(minCategory,maxCategory);

/**
* Create all the hyperlinks and add them to the dynamic panel.
*/
ArrayList<Hyperlink> hyperlinks = new ArrayList();
Hyperlink dynamicHyperlink = null;
for(int ii=0; null != counts && ii < counts.length; ii++) {
dynamicHyperlink = new Hyperlink();
dynamicHyperlink.setText(counts[ii].getName());
dynamicHyperlink.setActionListenerExpression(listenerMethod);
dynamicHyperlink.setActionExpression(actionMethod);
/**
* Get the font for this link which will be calculated based on the
* count and where it falls into a range for each bucket.
*/
String font = getFont(counts[ii],range);
dynamicHyperlink.setStyle(font);
dynamicPanel.getChildren().add(dynamicHyperlink);

}

The Finished Product

Running my sample project, you can see that we get a small cloud.

cloud

You can use my sample project for a good starting point.  I really wish I new how to create components.  This would make a great component wouldn't it!

( Jun 22 2007, 03:46:56 PM MDT ) Permalink Comments [2] del.icio.us | digg | technorati

20070607 Thursday June 07, 2007

NetBeans Visual Web Pack - Real World Apps Tip #2 - Dynamic Hyperlinks

In my last blog I covered creating Dynamic Content in NetBeans VWP (Visual Web Pack).  In this blog, I'll show you how to create "Hyperlink" components dynamically in VWP.

Creating the Hyperlink component and adding it is really not difficult.  But, hooking the Hyperlink component up to a Java backing beans is a different story.  This took me a while to figure out.

Creating the Hyperlink

Actually creating the "Hyperlink" component is easy.  Here's the code.

Hyperlink link = new Hyperlink();

Using the Hyperlink to Pass Data


As with most cases developing on the JavaServer Faces framework, you can't ignore the standard processing lifecycle.  Remember VWP uses a modified "Application Model" based off the one used by Java Studio Creator 2.  Why is this important?  In the case of a Hyperlink on a page, if you want something about the Hyperlink to be passed as data to the actual page bean method that will process the hyperlink click, you either need to use the SessionBean or the Hyperlink component itself.  The technique I'm about to show you uses the Hyperlink component.  Remember you can't use state on the page bean because a new instance of the page bean will be created on a postback.

To explain this, I'll use the example of the Plugin Portal when the categories are in list form rather than tag form.  Here's a snapshot of that area of the screen.

List

You can see that the Hyperlink has the category name and the number of items in that category.  In the Plugin Portal code, I needed the category name so I could show a category list of that category.  To pass that name to the method that would deal with the selection, I used the "Hyperlink.type" property.  The "type" property is used to set the MIME type of the hyperlink.  Since we are handling the link, we don't care about the MIME type so we can use this property to pass values.

So here is some code that creates the Hyperlinks using the type property.

        /**
* Now create the hyperlinks to add
*/
Hyperlink dynamicHyperlink1 = new Hyperlink();
dynamicHyperlink1.setText("list1(" + list1.size() + ")");
dynamicHyperlink1.setType("list1");
Hyperlink dynamicHyperlink2 = new Hyperlink();
dynamicHyperlink2.setText("list2(" + list2.size() + ")");
dynamicHyperlink2.setType("list2");
Hyperlink dynamicHyperlink3 = new Hyperlink();
dynamicHyperlink3.setText("list3(" + list3.size() + ")");
dynamicHyperlink3.setType("list3");

/**
* Create a grid panel so we have one column.
*/

HtmlPanelGrid listPanel = new HtmlPanelGrid();
listPanel.getChildren().add(dynamicHyperlink1);
listPanel.getChildren().add(dynamicHyperlink2);
listPanel.getChildren().add(dynamicHyperlink3);
/**
* Add the list grid panel to the layout panel.
*/
dynamicPanel.getChildren().add(listPanel);

Mapping the Hyperlink to Backing Beans Methods

Now to the difficult part, at least before knowing what I'm about to show you. :)  Many times you want Hyperlinks to invoke some action on your Java backing bean.  To accomplish this, you'll need to know how to create a "MethodExpression".  A Hyperlink component has two properties for dealing with an action:

The ActionExpression is the method JavaServer Faces uses to determine page navigation.  The ActionListenerExpression will wrap the Hyperlink in the event and let us use the "Type" field as a way to pass data.  When a user clicks on the Hyperlink, the methods will be called in this order.

  1. ActionListener method
  2. Action method
To create a MethodExpression, you need the "ELContext".  You can get the "ELContext" from the "FacesContext".  You use the "ExpressionFactory" from the "Application" to create the MethodExpression.

So here's the code to create the ActionListenerExpression.

       /**
* Create the MethodExpressions for the Hyperlinks
*/

/**
* First get the ExpressionFactory from the Application
*/

ExpressionFactory expressionFactory = this.getApplication().getExpressionFactory();

/**
* Next get the ELContext
*/

ELContext elContext = FacesContext.getCurrentInstance().getELContext();

/**
* Now create the MethodExpressions
*/
MethodExpression listenerMethod = expressionFactory.createMethodExpression(elContext,
"#{Page1.category_action}",
null,
new Class[] {ActionEvent.class});

MethodExpression actionMethod = expressionFactory.createMethodExpression(elContext,
"#{Page1.category_action}",
String.class,
new Class[] {});

dynamicHyperlink1.setActionListenerExpression(listenerMethod);
dynamicHyperlink1.setActionExpression(actionMethod);
dynamicHyperlink2.setActionListenerExpression(listenerMethod);
dynamicHyperlink2.setActionExpression(actionMethod);
dynamicHyperlink3.setActionListenerExpression(listenerMethod);
dynamicHyperlink3.setActionExpression(actionMethod);
Lastly, you need to create the methods that will be invoked by clicking the link.  These methods will go on the page bean.  

Let me talk about the "createMethodExpression" method signature for a moment because that's important for understanding the methods.

The first parameter is the ELContext.  We've already discussed that.

The second parameter is the binding expression.  If you look at the code above, you'll see that the page is hard coded in the binding expression.
#{Page1.category_action}
You will also notice the method name "category_action".  

The third parameter is the method return type.  You'll notice in the code above for the ActionListener, we don't define a return type.  For the Action we do define a "String".  This method signature, "String action_method()" called by an action will cause JavaServer Faces navigation to check the outcome to see if a navigate is needed.

The fourth parameter of the "createMethodExpression" call is the list of arguments that will be passed to the method.  Again notice for the ActionListener the "ActionEvent" is passed.  Remember this is how we will pass data.

Here are the methods you need to define on the page.

    public String category_action() {
return "page2";
}

public void category_action(ActionEvent ae) {
String selectedListName = ((Hyperlink)ae.getComponent()).getType();
getRequestBean1().setSelectedList(selectedListName);
return;
}

You can see the "return "page2"" in the Action method.  This corresponds to this navigation file which will cause a page navigation to "Page2".

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
<navigation-rule>
<from-view-id>/Page1.jsp</from-view-id>
<navigation-case>
<from-outcome>page2</from-outcome>
<to-view-id>/Page2.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/Page2.jsp</from-view-id>
<navigation-case>
<from-outcome>case1</from-outcome>
<to-view-id>/Page1.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>

Conclusion

So here's what the page will look like.

dynalink1

In my next blog, I'll use this blog and the previous blog to show you how to create a "tag cloud".

You can download my sample "DynamicHyperlinks" project to get started.  As before, I used NetBeans 5.5 with Visual Web Pack.


Posted by David Botterill ( Jun 07 2007, 12:26:01 PM MDT ) Permalink Comments [2] del.icio.us | digg | technorati

20070604 Monday June 04, 2007

NetBeans Visual Web Pack - Real World Apps Tip #1 - Dynamic Content

I promised I would start blogging about my experiences developing the NetBeans Plugin Portal when I gave my BOF at JavaOne this year.  So this blog is the start of that series.  To get some background on the project, you really should look through my slides for the BOF.

There seemed to be the most interest around building a "Tag Cloud" using NetBeans VWP (Visual Web Pack).  So I thought I would start with that topic.  In order to do tag clouds in VWP, you need to understand how to create two things first, dynamic content and dynamic hyperlinks.  This blog will cover the details of creating dynamic content with NetBeans VWP.  I'll then follow up with blogs for dynamic hyperlinks and finally tag clouds.

I do plan to put the source code for the Plugin Portal out on netbeans.org soon but I have to get through another phase and do some cleanup first.  In order to demonstrate these topics, I'll be building simplified projects to be downloaded.

First, what is "Dynamic Content"?  My use of this term focuses on the distinction between the design time UI design and the run time UI.  Content that can not be visually created during design time is content that I refer to as "Dynamic Content" or content that is created during run time.

Representing the Content in the Design Time

One of the greatest features of using VWP is the visual designer.  The designer gives you a pretty good idea what the page will look like at runtime.  So obviously you'd like to use the designer to even help with the dynamic content.  To do this, use a "Layout Panel" as a placeholder for the dynamic content.  Here's a screenshot of the project we will build.  You can see the outline of the "Layout Panel" which I named "dynamicPanel".

layout panel

Creating the Content at Run Time

Now that you have a placeholder for the dynamic content, "dynamicPanel", you can create the content at run time.  We'll target the "preprocess" method of the page backing bean.  For more information on the VWP application model, please see the article, "The Java Studio Creator 2 Application Model".  As you may already know, VWP incorporates most of the Java Studio Creator 2 functionality. The application model in VWP is the same as Java Studio Creator 2.

For this first example, let's simply add a button as dynamic content.  Here's the code to do that.

    public void prerender() {
/**
* First clear out the layout panel
*/
if(null != dynamicPanel.getChildren()) {
dynamicPanel.getChildren().clear();
}
/**
* Now create the button to add.
*/
Button dynamicButton = new Button();
dynamicButton.setText("My Button");
/**
* Add the button to the layout panel.
*/
dynamicPanel.getChildren().add(dynamicButton);
}

Note 1 - If you don't know the type for the component you want to add, visually add the component to the designer and look at the Java source to find the type.

Note 2 - The content you add MUST be a subtype of "UIComponent".

Conclusion

Here's what our page will look like.

page

Now that I have covered the basics of creating dynamic content, in my next blog I'll cover creating dynamic hyperlinks.


You can download the sample "DynamicContent" project I used to get started.  I used NetBeans 5.5 with Visual Web Pack installed to create the project.


Posted by David Botterill ( Jun 04 2007, 01:19:48 PM MDT ) Permalink Comments [2] del.icio.us | digg | technorati