Monday, June 23, 2008

Updated enum display values code

I previously posted about how to automatically format an enum value by overriding the enum's toString() method. The code works, but it doesn't scale very well and it's not as robust as it could be, so I've pulled it out into it's own method, and added a "case exception list" feature, so it can be called from any enum, and allows for more flexibility:

public class EnumUtils {
public static String displayFormat(String enumName, String... caseExceptions) {
String[] splitNames = enumName.split("_");
String caseExceptionName = "";
Boolean caseException = false;

for(String name : caseExceptions) {
String parsedName = name.replace(" ", "_");

if(enumName.equalsIgnoreCase(parsedName)) {
caseExceptionName = parsedName.replace("_", " ");
caseException = true;
}
}

StringBuffer fixedName = new StringBuffer();

if(caseException) {
fixedName.append(caseExceptionName);
} else {
for(int i = 0; i < splitNames.length; i++) {
String firstLetter = splitNames[i].substring(0, 1).toUpperCase(),
restOfWord = splitNames[i].substring(1).toLowerCase(),
spacer = i == splitNames.length ? "" : " ";

fixedName.append(firstLetter).append(restOfWord).append(spacer);
}
}

return fixedName.toString();
}
}

which is called from the enum like this, for example:
@Override
public String toString(){
return EnumUtils.displayFormat(name(), "NT Servers", "SQL Server");
}
The method takes in the name of the enum value, as usual, but also allows the developer to specify a variable-length exception list, thanks to Java's "varargs" feature, which lets you accept an array of any basic type as the last argument. All you need to do in the method is specify the type along with ellipses (...), and it takes care of the rest for you. The code allows the developer to specify the enum value with a space between words, or with "_"'s. BTW, it also works fine with no exceptions:
return EnumUtils.displayFormat(name());
If you've seen my original code in the post below, you'll also noticed that I've changed StringBuilder to StringBuffer, and changed the String.concat()s to StringBuffer.append()s. StringBuilder is actually just meant for single-threaded operations, so while it's probably safe to use it here, it could potentially wreak havoc. The append()s are for miniscule performance gains (though I haven't run tickcount tests yet to actually confirm that this method is faster). I've also defined the new strings in the second for loop with commas between them, instead of re-typing "String" three times. I heavily doubt there's a performance gain here, as I assume the compiler sees both the same, but it's slightly faster to type :-)

BTW, I'm now using the Blogger Syntax Highlighter to display formatted code, as it looks a bit nicer, and has native support for Java.

Thursday, June 19, 2008

Binding an enum to a jComboBox with user-friendly name values (Java Swing)

Binding an enum to a jComboBox is easy enough (though, as far as I know, not possible using the GUI tools that are available with NetBeans 6.1). First, of course, define the enum:

enum Timeframe {
THIS_WEEK,
THIS_MONTH,
THIS_YEAR
}

Then link it to the combo box:

ComboBoxModel cbModel = new DefaultComboBoxModel(Timeframe.values());
timeframeComboBox.setModel(cbModel);

This works fine, except that you get the not so user-friendly "THIS_WEEK", "THIS_MONTH", etc. values displayed in the dropdown. To fix this, the easiest way is to override the "toString()" method of the enum, so the modified enum becomes:

enum Timeframe {
THIS_WEEK,
THIS_MONTH,
THIS_YEAR;

@Override
public String toString(){
String[] splitNames = name().toLowerCase().split("_");
StringBuffer fixedName = new StringBuffer();

for(int i = 0; i < splitNames.length; i++) {
String firstLetter = splitNames[i].substring(0, 1).toUpperCase(),
restOfWord = splitNames[i].substring(1),
spacer = i == splitNames.length ? "" : " ";

fixedName.append(firstLetter).append(restOfWord).append(spacer);
}

return fixedName.toString();
}
}

This code first lowercases everything, then splits it up into separate words based on the "_" character. It then loops over all the words, isolates and capitalizes the first letter, and isolates the rest of the word in a separate variable.

Next, a space is added if there are more words left to be looped over. Finally, everything is re-assembled and added to the StringBuilder variable that's returned as the "fixed" name. BTW, if anyone know of a more efficient way to do this, I'd love to hear about it. Java's regex doesn't appear to support the "L" flag or anything similar, otherwise I could have done this much more efficiently with one regex replace.

In any case, no other changes are necessary, as toString() only affects the display of the enum and not its underlying values, so you can then use code that uses the enum as usual:

private void timeframeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {                                                  
JComboBox cb = (JComboBox)evt.getSource();
Timeframe timeFrame = (Timeframe)cb.getSelectedItem();
Date now = new Date();

switch(timeFrame) {
case THIS_WEEK:
//processing code...
BTW, it's interesting that Java's "switch/case" only supports ints, though I guess it does kind of force you into using enums for this type of thing, which, a lot of the time, would be the preferred method over using plain strings. (As you're probably aware, enums underlying real values are ints).

Tuesday, June 17, 2008

Using row highlighters in SwingX 0.9.3

I was excited to read about row highlighting in the JXTable SwingX component, and found a number of tutorials showing how to implement this. But, all of them show how to do it the same way, and none of them work any longer (at least as of v 0.9.2+). Here's the example code from the SwingLabs tutorial:

jxTable.setHighlighters(new HighlighterPipeline(
new Highlighter[]{ AlternateRowHighlighter.classicLinePrinter }));
There is no more "HighlighterPipeline", nor is there "AlternateRowHighlighter". I couldn't find any other up-to-date tutorials on the subject, so I did a little digging in the SwingX JavaDocs, and was able to put together the following code that accomplishes the same thing.
import java.awt.Color;
import org.jdesktop.swingx.decorator.*;
...
ColorHighlighter highlighter = new ColorHighlighter();
highlighter.setHighlightPredicate(HighlightPredicate.ODD);
highlighter.setBackground(Color.getHSBColor(0.62f, 0.01f, 0.92f)); //very light blue

DetailsTable.setHighlighters(highlighter);
Not much to explain here, just a different way of doing it. In addition to a number of other features, HighlightPredicate also supports "ROLLOVER_ROW", which works as you'd guess. "ROLLOVER_ROW" has also made the following unnecessary:
jxTable.setRolloverEnabled(true);
Code changes are obviously one of the problems with using pre-release code, though I've had great luck with SwingX components so far concerning stability. It's been worth the risk for me so far, though I'm not relying on the extra functions that SwingX provides for anything "mission critical" - just for row sorting, filtering, coloring, packing, etc.. JXTable inherits from the standard JTable, so if I decide to remove SwingX, there should be minimal pain involved (I hope).

See screenshots of your web site with different browsers

I've been looking for a site like this for quite a while. BrowserShots.org lets you select from 67 different browsers/versions, and allows you to select parameters like screen size, color depth, and what features are enabled in the browser.

It takes a little while for the results to come back (at least five minutes or so, though you can begin browsing the first few screenshots almost immediately), but this is a really helpful service that I could have used years ago.

Modifying a database schema in NetBeans 6.1

If the only changes to your schema are within existing tables, you're in luck, as you can just open the "META-INF" folder, right click on the schema, and choose "Recapture Schema from Database". After doing this, make sure to rebuild the project ("Build" --> "Build Main Project" from the top menu). Now the new schema info will be available in beans binding menus, etc.

However, the only way I could figure out how to change a schema that involves a new table is to create a new schema, delete the old one, and modify any code that references the old schema. "Recapture..." does not retrieve new tables, and there's no way I could find to actually edit the schema, without diving into the XML code (I've seen it - it's not worth it, and I'm not usually afraid to edit complex XML).

To create a new schema, right click on the "META-INF" folder, and select "New" --> "Other..." --> select the "Persistence" folder, and "Database Schema". Name it, select your database, select the tables you want to add. Next, either manually edit or delete and re-create any Entity Classes. Rebuild the project, and you're set.

I assume/hope an "Edit Schema" function is coming to NetBeans someday soon. "Update Entity Class" would be nice, too.

Wednesday, June 11, 2008

Retrieving a user's email address from Active Directory

An application I'm working on requires complete user management through Active Directory. So I'm slowly building up an "ADUtils" support class to wrap up all the necessary AD querying. I've found a decent amount of ADSI examples online, though finding specific information, and extracting best practices from these has been difficult.

Being rather new to AD, I don't make any claims that the code below follows all best practices, though I'll try to explain what I've done and why I've done it that way. If you know of a better way to accomplish the following, please let me know.

    /// <summary>
/// Retrieves the user's email address from AD
/// </summary>
/// <param name="user">User's NT account name without domain prefix</param>
/// <returns>User's email address</returns>
public static string GetEmailAddress(string user) {
DirectoryEntry entry = new DirectoryEntry("LDAP://DC=yourdomain,DC=com");
DirectorySearcher searcher = new DirectorySearcher(entry);

searcher.Filter = String.Format("(SAMAccountName={0})", user);
searcher.PropertiesToLoad.Add("mail");

SearchResult result = searcher.FindOne();

string emailAddress;

try {
emailAddress = result.Properties["mail"][0].ToString();
} catch (ArgumentOutOfRangeException e) {
emailAddress = String.Empty;

//TODO: log/display error
}

return emailAddress;
}

  • The LDAP address does not specify the domain controller. This allows AD to intelligently choose which DC to use.

  • By explicitly stating what properties to load, you save the server from loading them all.

  • "searcher.FindOne()" does exactly what it says and just loads one result. I'm not sure of the performance benefit of using this vs. "searcher.FindAll()" if there's only one result anyway, but this is certainly better form if you're expecting only one result.

  • Lastly, if the field is left blank in AD, you will get an ArgumentOutOfRangeException instead of a blank field back, so it's a good idea to explicitly handle it.

Using my "GetUsersInGroup" method, you can use the "GetEmailAddress" method to get all group members' email addresses:

using System.Configuration;
...
string LDAPGroupAddress = "LDAP://CN=yourgroup,CN=Users,DC=yourdomain,DC=com";
CommaDelimitedStringCollection emailList = new CommaDelimitedStringCollection();

ADUtils.GetUsersInGroup(LDAPGroupAddress).ForEach(delegate(string member) {
emailList.Add(AuthUtils.GetEmailAddress(member));
});
...
mail.To.Add(emailList.ToString());

And since we're on the topic of performance, the generic List method "ForEach" is faster than the equivalent regular "foreach", though "for" is actually the fastest, but there's really not much of a difference at all unless you're dealing with a huge number of iterations.

As a side note, this is my first post using Windows Live Writer, and with the incredibly helpful "Insert Formatted Code" add-in. I'm very impressed with both.

Tuesday, June 10, 2008

Dead power supply

My home desktop computer (Dell Dimension 4500) stopped working last night, but before it died, it blue screened on startup with the following:

0x0000008E
(0xC0000005, 0x805677CB, 0xF300CB34, 0x00000000)

According to a number of Google searches, this error can actually mean a few different things, including RAM and driver issues, but the one that made the most sense was that the power supply was weak, since on the next restart attempt, it didn't actually restart (just a green light when the power button is pressed - no fan, no display on the monitor). Also, the power supply fan had been very loud for a few weeks previous. So it all fits.

I don't have a spare power supply to test with, so I ordered another one last night. Hopefully that's the only problem. I really don't want to get a new computer before Nehalem comes out, and preferably not before Windows 7 comes out as well, but my computer is going on seven years, and I've resuscitated it a few times already, so I'm not holding my breath.

UPDATE (6/12): New power supply arrived (free delivery, only took two days - thanks, 911forPCs.com!). I installed it, and now everything works again. But, the CPU fan is still a bit loud, so I'm going to try greasing it, or replacing it if necessary.

Monday, June 9, 2008

Using the Java Persistence API with Swing in NetBeans 6.1 (part 1)

I have yet to find a good tutorial on how to get started using JPA with the new version of NetBeans that takes advantage of Beans Binding and other helpful new features. NetBeans has a lot built in to help you through it, but configuration is not obvious, and there are a few tricky parts along the way. So hopefully the steps below will at least get you started.

This tutorial will focus on using JPA without EJB (JPA is part of the EJB spec for now, but was built to be used with or without it - future versions will be separate from the EJB spec to better reflect this). I'm also assuming you have a database already set up, and are just looking to connect it to your application.

  1. Create a new project in NetBeans 6.1 (Ctrl+Shift+N)
  2. Choose "Java" --> "Java Desktop Application"
  3. Name the application, and select "Basic Application"
  4. Right click on your project, and select "New" --> "Entity Classes from Database..." and select your database connection (see my previous post about connecting to SQL Server)
  5. Change the class names if necessary, and click on "Create Persistence Unit..."
  6. The defaults here are fine - btw, the latest versions of TopLink and Hibernate both fully support JPA. I'm using TopLink for this project.
  7. Back on the main design screen, locate "Java Persistence" in the "Palette" section (normally on the right hand side, at the bottom of the Palette)
  8. Drag an "Entity Manager", "Query", and "Query Result" instance onto the design area, and rename them if you choose.
  9. Click on the instance of the "Query" in the "Inspector" (bottom left-hand side by default), and view its properties (bottom right-hand side by default).
  10. Click on the elipses "..." next to "query" on the properties pane, and type in a valid JPQL query. JPQL is similar in syntax to SQL, but it deal directly with objects. The JPQL query equivalent of


    SELECT *
    FROM WeeklyDetail
    INNER JOIN...
    would actually be


    SELECT wd
    FROM WeeklyDetail wd
    "wd" is just like a SQL alias (i.e. SELECT wd.* FROM WeeklyDetail wd), but it is required in JPQL.

    The nice part about querying objects is that for simple related objects/tables, you don't need to deal with JOINs. The above JPQL query actually returns all related objects (from different underlying SQL tables). To adjust for performance, you can explicitly state how the data is "fetched" - basically "all at one time" or "on demand". However, this is control in the entity classes.

    There's volumes more to JPA - to really get a handle on how to work with it, I suggest reading "Pro EJB 3: Java Persistence API" by Mike Keith and Merrick Schincariol.
  11. Make sure in "other properties" below the query field, that your Entity Manager is selected as the "entityManager".
  12. Select your "Query Results" instance (I've called mine "weeklyDetailsQueryResult"), and view it's properties.
  13. Select your query that you just worked with, and check "modifiableWrapper" and "observable".
  14. Under the "Code" tab (still in the properties), in the "Type Parameters" field, type the name of your package followed by a "." and the entity class name. Technically, the package name should be different casing from your application name (Pascal case for the application, camel case for the package.

    NOTE: If you type the casing incorrectly, it will appear to work until you run the project.
  15. Drag a jTable onto the design area (or jxTable if you're using SwingX (see my previous post) and want to have built-in column sorting).
  16. Right click on the table, and select "Bind" --> "elements".
  17. For the binding source, choose the query result you just created, and choose the columns you would like displayed in the table.
  18. Now when you run the project, the table should populate with the data from your database.
Part 2 of this tutorial will address how to update and delete data from within the table.

NOTE: screen captures showing each step coming soon

Friday, June 6, 2008

sp_MSForEachTable

I had to improve performance on a just-restored database, and remembered of one of my favorite undocumented SQL Server commands, sp_MSForEachTable. In the below example, the "?" is the variable for where the table name should go in the statement you want to execute:


EXEC sp_MSForEachTable "UPDATE STATISTICS ?"
Another quick example - to print out all the table names in a db, just run:


EXEC sp_MSForEachTable "PRINT '?'"
FYI, there's also sp_MSForEachDB that lets you do the same operation on all databases on the server. Learning this alone was worth the price of Brian Knight's "Admin 911: SQL Server 2000" book.

Thursday, June 5, 2008

Baluster End Spacing Calculator in C#

I'm working on putting up balusters (spindles) on my deck at home, and since all the posts are spaced differently, I needed to calculate the proper end spacing for each section. Granted, it's easy enough to do this by guessing at the first length, then physically marking out each baluster width and spacing on the actual deck, and then adding the start spacing with the end spacing and dividing by two. And that's probably what I'd normally do, but I wanted to take a little break, so I made a quick app to figure this out for me.

It's a very simple app, and took about 10 minutes to write, but here's the interesting parts in case anyone is interested:


/// <summary>
/// Determines the number of balusters needed based on the top railing width
/// </summary>
/// <returns>Baluster count</returns>
public int CalculateBalustersNeeded() {
int numberOfBalusters = Convert.ToInt32(Math.Ceiling(
(this.topRailingWidth - this.spacing) / (this.balusterWidth + this.spacing)));

return numberOfBalusters;
}
and


/// <summary>
/// Determines the spacing between the post and the baluster.
/// </summary>
/// <param name="balusterCount"># of balusters used.</param>
/// <returns>Spacing at each end in inches</returns>
public float CalculateEndSpacing(int balusterCount) {
//all balusters + spacing without end spacing
float totalUnitWidth = (balusterCount * this.balusterWidth)
+ (this.spacing * (balusterCount - 1));
float endSpacing = (this.topRailingWidth - totalUnitWidth) / 2;

return endSpacing;
}
Nothing too exciting, really. And Excel probably would have been a better choice than a console app. But since it's all done with more or less "proper" objects (these methods are part of a "RailingSection" class generic collection that's part of a "Deck" object), I can now easily expand this into a full-blown deck design software package. Right. I guess I just can't get enough of coding at work...

UPDATE (6/13): Wow, this post is getting a lot of hits by people searching for a baluster calculator - here's the compiled EXE file: BalusterCalc.exe. You'll need the .NET framework 2.0 or higher to run it.