Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Tuesday, July 1, 2008

JTextArea setEnabled strangeness (Java Swing)

NetBeans 6.1 automatically wraps a JScrollPane around a JTextArea, and handles all the details of setting it up nicely. But, if you need to work with all your components at once (i.e. to tick the "setEnabled" property on each), you'll find the following code works on everything but the JTextAreas (and probably all other nested components):
for(Component component : ProjectPanel.getComponents()) {
component.setEnabled(false);
}
I haven't found a good way around this yet, as the following addition to the loop fails as well:
//use reflection to get the class type
if(component.getClass().equals(JScrollPane.class)) {
JScrollPane scrollPane = (JScrollPane)component;

for(Component subComponent : scrollPane.getComponents()) {
subComponent.setEnabled(false);
}
}
Debugging reveals that the the JTextArea isn't seen as a component here (things like the scroll bar and viewport are, though). getComponents() doesn't loop over children, so maybe this does work two or three levels deeper, but that's getting a bit ridiculous.

Until I figure out a better way, I'm setting the values explicitly using the following method:
textArea.setEnabled(false);
textArea.setBackground(UIManager.getColor("TextArea.disabledBackground"));
The "setBackground" method is needed here (at least on Windows XP using the "Classic" theme), as the background isn't automatically set to the disabled color when setEnabled is set to "true". Most other components automatically handle this for you. This also means when you re-enable it, you need to set it back to the default enabled color:
textArea.setBackground(UIManager.getColor("TextArea.background"));
BTW, I found a good article on using UIManager here that explains how to use and manipulate UI settings.

In any case, I found an official Java bug report from 1998 concerning what appears to be the exact enabled/disabled problem I ran into - the bug's status is "6-Fix Understood, request for enhancement". Though the suggested fix actually didn't work for me...

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).

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.

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

Tuesday, May 20, 2008

Free J2EE online course

The next run of the J2EE Programming course in the excellent "Java with Passion" series starts on July 1st. This is all put together by Sang Shin, a technology architect at Sun Microsystems.

More details here:
http://www.javapassion.com/j2ee/

I've just signed up.

Wednesday, May 14, 2008

Java --> .NET rough equivalents

To help me understand Java concepts better, I've made a comparison to the rough equivalents in .NET.

Here's the list so far:
  • Java ~= C# or VB.NET
  • J2EE/JEE (Java Enterprise Edition) ~= .NET framework
  • Swing ~= Windows Forms
  • AWT ~= VB6 Forms?
  • Beans Binding ~= data binding
  • JPA (Java Persistence API) ~= ORM via ADO.NET Entity Framework / LINQ
  • EJBQL/JPQL (Enterprise JavaBeans Query Language/Java Persistence Query Language) ~= LINQ
  • JDO ~= ADO.NET
  • JSF (Java ServerFaces) ~= ASP.NET
  • JSP (Java Server Pages) ~= plain ASP
  • Hibernate, JUnit, Spring all have direct .NET equivalents (NHibernate, NUnit, Spring.NET)
I'm still getting a grasp on a lot of this stuff, so please let me know if I've misrepresented anything. I'll also add to the list as I think of more things.

UPDATE: Paul Sasik posted a question about .NET/Java comparisons at StackOverflow.com, and got a lot of great responses, which update and expand this list considerably (see http://stackoverflow.com/questions/2526024/analogues-of-java-and-net-technologies-frameworks). Thanks for linking to my initial blog post, Paul.

Installing SwingX in NetBeans 6.1

SwingX adds some really nice UI and other improvements to Swing. It's sponsored by Sun, and may be released in a future version of Java, but appears stable enough to use in projects now.

There's a tutorial on how to set it up, but it's missing some important content, so here's how I did it:

1. Download and unzip from http://swinglabs.org/downloads.jsp
2. Place swingx-x.x.x.jar file (from swingx/dist/ folder) in a place that you won't delete it later.
3. Open your project in NetBeans, right-click on "libraries", and choose "Add Library..."
4. Click on "Create...", give it a name (i.e. "SwingX"), and select "Class Libraries".
5. On the next dialog box, under the "Classpath" tab, click "Add JAR/Folder..." and locate the swingx-x.x.x.jar file from step 2.
6. Add the new library to your project.
7. To get the visual items in the palette, go to a jFrame or other class that has a design view, right-click anywhere on the Palette view, and select "Palette Manager...".
8. Click on "New Category..." and type "SwingX" (or whatever you want to call it).
9. Click on "Add from JAR...".
10. Find your swingx.x.x.x.jar file again, and on the next screen, select all the components you want to be displayed.
11. Finally, select the category folder you just created in step 8.

Tuesday, May 13, 2008

Working with SQL Server in NetBeans 6.1

The JDBC-ODBC bridge wasn't recognizing any of the primary keys I had set up in SQL Server. After lots of head banging, here's the solution I came up with:

1. Download the appropriate driver. SQL Server 2000 requires the SQL Server 2000 JDBC driver (http://www.microsoft.com/downloads/details.aspx?FamilyId=07287B11-0502-461A-B138-2AA54BFDC03A&displaylang=en).
SQL Server 2005 download: (http://www.microsoft.com/downloads/details.aspx?familyid=C47053EB-3B64-4794-950D-81E1EC91C1BA&displaylang=en)
3. After installing, right-click on "Libraries" in your project, and choose "Add Library...". Next, give it a name (i.e. SQLServer2000), and select "Class Libraries".
4. On the next screen, find the JAR files (should be in C:\Program Files\Microsoft SQL Server 2000 Driver for JDBC\lib\), and add them under the "Classpath" tab. It will now be available under "Libraries" for future projects.
5. You can now create a connection to a specific database under the "Services" tab (next to "Projects" and "Files" in the top left of the screen). Select "Microsoft Sql Server 2000 (Microsoft Driver)" and format the "Database URL" like this:

jdbc:microsoft:sqlserver//YOURSERVER:1433;DatabaseName=YOURDATABASE

1433 is the default port, though your DBA may have changed it.

I posted a simpler version of this on the NetBeans.org FAQ page - they had the following title with no content on the answer page:

"Cannot Select Tables From MsSql 2000 Because It Says No Primary Key Next To Each Table But The Tables DO Have A Primary Key. What Do I Do?"

Looks like somebody was really frustrated, and posted a question in the hopes that somebody would answer it :-) Of course I was really frustrated seeing a question with no answer, so I added one after I figured it out myself.