Monday, October 31, 2011

JLabel Get Tooltip

   package org.best.example;
    /*
            JLabel Get Tooltip Example
            This java example shows how to get tooltip text for JLabel using
            getToolTipText method of Java Swing JLabel class.
    */    
    
    import javax.swing.JApplet;
    import javax.swing.JLabel;
    
    public class JLabelGetTooltipExample extends JApplet{
                  
          
            public void init(){
                    /*
                     * To create JLabel use
                     * JLabel (String caption) constructor
                     * of JLabel class.
                     */
                  
                    JLabel label1 = new JLabel("JLabel Get ToolTip Example.");
                          
                    //add label to applet
                    add(label1);
                  
                    /*
                     * To get ToolTip text of JLabel use,
                     * String getToolTipText()
                     * method of JLabel class.
                     */
                  
                    String toolTipText = label1.getToolTipText();
            }
    
    }

Validation with pure Java - 1

Build a solid foundation for the data-validation framework with the core Java API

The idea of constrained properties in Java is not new. Since JDK 1.1, a legion of JavaBeans developers has used a powerful framework found in the java.beans package to enforce data-validation rules. Unfortunately, the rest of us -- who are not in the business of crafting "reusable software components that can be manipulated visually in a builder tool" -- quite often attempt to reinvent the wheel by abandoning the core Java approach in favor of various proprietary solutions. The externalization of data-validation rules is the area where you can see the most creativity. An interesting XML-based approach was recently proposed in the "Validation with Java and XML Schema" series of JavaWorld. While admiring XML technology, I believe that Java has everything needed to solve the issue in the most elegant way. To show you, I invite you to rediscover some of the real gems found in the java.beans package. They will help you build a few useful classes and learn a couple of interesting tricks.
The logic behind constrained properties in Java is quite simple. Before accepting a new data value, the object (owner of the constrained property) makes sure it is accepted by all interested parties that may veto it. Such a "request for approval" is delivered to every registered java.beans.VetoableChangeListener in the form of a java.beans.PropertyChangeEvent object. If one or more vetoes have been issued, the proposed data value is rejected. A veto is presented by a java.beans.PropertyVetoException. Generally speaking, the complexity of the rules enforced by those listeners has no limit and depends only on the project's requirements and the developer's creativity. The objective of this exercise is to learn how to deal with the most generic data-validation criteria that you can apply to most objects.

Sunday, October 30, 2011

Steer clear of Java pitfalls - 3


Pitfall 3: Don't mix floats and doubles when generating text or XML messages
While developing an order execution system for an online brokerage, I stumbled across a serious bug that incorrectly converted certain values from doubles to strings. Here is the scenario: The Website presents a stock-trade form to the user. A Java servlet processes the form and sends the trade information to the order execution server, a Java RMI server. The Java RMI server formats the message as either XML or another text format -- the common message switch (CMS) format, for example -- and passes it to one of several executing agents. One of the fields in the stock-trade message is the stock price, which is stored as a double. For certain double values, the Java platform incorrectly converts the price when formatting the order message, and the trade is rejected. Customers don't like that!
What if this was embedded software in a medical device, and the double value represented the amount of radiation administered to a patient? A low-level bug like this can be extremely dangerous.
Below is an applet that simulates the above scenario and generates two stock transaction messages. The first price formats correctly, while the second value -- 100.28 -- formats incorrectly.
Message 1...
            BUY
            SUNW
            1000
            98.5

Message 2...
            BUY
            SUNW
            1000
            100.27999877929688
I originally labeled this problem a bug, and I did find that Sun's JDC bug database reports it several times, in numbers 4030639, 4087318, 4068996, and 4169083. Unfortunately, these similar bugs are described inconsistently. Some are reported fixed, others are not even considered bugs, and one is labeled as a bug "in progress." Unfortunately for the many application developers who must generate XML messages that contain double values, this bug exists in JDK 1.1, 1.2, and 1.3. Thus I consider it a pitfall.
The following is a simple example of the bug. The problem lies in converting a float to a double, prior to converting to a String. This occurred in the brokerage software when one developer primarily used floats for decimals, but another implemented doubles. That caused the bug to surface; this crashed the executing agent's stock trading system, which received our incorrectly formatted trade message. Notice that the method genTradeMessage() uses a float to represent the price. The purpose of genTradeMessage() is to generate a simple text XML order message. In turn, genTradeMessage() calls formatStockPrice(), which takes a double as the price parameter. Here is the invocation of genTradeMessage() that fails:
String msg2 = genTradeMessage("SUNW", "BUY", 1000, 100.28f);
Notice that a float of 100.28 is passed into genTradeMessage(). Here is the code for genTradeMessage() and formatStockPrice():
    public String genTradeMessage(String symbol,
                                  String action,
                                  int shares,
                                  float price)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        pw.println("");
        pw.println("\t" + action + "");
        pw.println("\t" + symbol + "");       
        pw.println("\t" + shares + "");
        pw.println("\t" + formatStockPrice(price));       
        pw.println("");
        return sw.toString();
    }
   
    public String formatStockPrice(double d)
    {
        return "" + d + "";   
    }
 There are two workarounds to this problem. One solution is to implement only doubles or only floats in your APIs. That would mean rewriting formatStockPrice() to use a float. Here is that code:
    public String formatStockPrice(float f)
    {
        return "" + f + "";   
    }
 Below is the GoodTradeMessageApplet that features the revised formatStockPrice().
Message 1...
            BUY
            SUNW
            1000
            98.5
            98.5
            98.5

Message 2...
            BUY
            SUNW
            1000
            100.28
            100.28
            100.27999877929688
Another potential workaround is to calculate using doubles, but cast to float when converting to a String. Of course, this solution is only viable if you are not losing precision on the cast.
Here is that version of formatStockPrice():
    public String formatStockPrice(double d)
    {
        float f = (float) d;
        return "" + f + "";   
    }
 This pitfall can also trip you up when you use JDBC. In the aforementioned order execution system, trades were stored in an SQL server database. When retrieving the day's trades to format a report, the getString() method would incorrectly format the price value -- as an 8-byte SQL float type -- from the database. Here is the JDBC call that would return the erroneous value:
String sprice = rslt.getString("price");

A run of the program produced the following results:
  • Getting a connection to jdbc:odbc:CustomerTrades...
  • Symbol: SUNW
  • Action: BUY
  • Price as String: 91.090000000000003

The solution is to use the getFloat() method in the ResultSet class to retrieve the value as a float, which then properly converts to a String. Here is the replaced code:
float price = rslt.getFloat("price");

A run of TestGetPrice2.java produces:
  • Getting a connection to jdbc:odbc:CustomerTrades...
  • Symbol: SUNW
  • Action: BUY
  • Price as Float: 91.09

JLabel Horizontal Text Position

   package org.best.example;
   /*
            JLabel Horizontal Text Position Example
            This java example shows how to set and get horizontal position of text relative to
            the image using Java Swing JLabel class.
    */         
    import javax.swing.ImageIcon;
    import javax.swing.JApplet;
    import javax.swing.JLabel;
    
    public class JLabelHorizontalPositionExample extends JApplet{
    
            public void init(){
                    /*
                     * Create an icon from an image using
                     * ImageIcon(String imagePath, String description)
                     * constructor.
                     */
                  
                    ImageIcon icon = new ImageIcon("images/copy.gif");
                  
                    /*
                     * To create a JLabel with image icon and text use,
                     * JLabel(String text, Icon icon, int horizontalAlignment)
                     * constructor of JLabel class.
                     */
                  
                    JLabel copyLabel = new JLabel("Copy", icon, JLabel.CENTER);
                  
                    /*
                     * Add label to an applet
                     */
                  
                    add(copyLabel);
                  
                    /*
                     * To get horizontal position of JLabel's content relative to it's
                     * image use,
                     *
                     * int getHorizontalTextPosition()
                     * method of JLabel class.
                     *
                     * Return value is one of the following constants,
                     * LEFT, CENTER, RIGHT, LEADING or TRAILING.
                     */
                  
                    int position = copyLabel.getHorizontalTextPosition();
                  
                    /*
                     * To set horizontal position of text relative to it's image use,
                     * void setHorizontalTextPosition(int position)
                     * method of JLabel class.
                     *
                     * Possible value is one of the following constants,
                     * LEFT, CENTER, RIGHT, LEADING or TRAILING.
                     */
                  
                    copyLabel.setHorizontalTextPosition(JLabel.RIGHT);
            }
    }

Saturday, October 29, 2011

Steer clear of Java pitfalls - 2


Pitfall 2: A misleading StringTokenizer parameter

This pitfall, also a result of poor naming conventions, revealed itself when a junior developer needed to parse a text file that used a three-character delimiter (his was the string ###) between tokens. In his first attempt, he used the StringTokenizer class to parse the input text. He sought my advice when he discovered what he considered to be strange behavior. The applet below demonstrates code similar to his.
input: 123###4#5###678###hello###wo#rld###9
delim: ###
If '###' treated as a group delimiter expecting 6 tokens...
tok[0]: 123
tok[1]: 4
tok[2]: 5
tok[3]: 678
tok[4]: hello
tok[5]: wo
tok[6]: rld
tok[7]: 9
# of tokens: 8
The developer expected six tokens, but if a single # character was present in any token, he received more. He wanted the delimiter to be the group of three # characters, not a single # character.
Here is the key code used to parse the input string into an array of tokens:
    public static String [] tokenize(String input, String delimiter)
    {
        Vector v = new Vector();
        StringTokenizer t = new StringTokenizer(input, delimiter);
        String cmd[] = null;
        while (t.hasMoreTokens())
            v.addElement(t.nextToken());
        
        int cnt = v.size();
        if (cnt > 0)
        {
            cmd = new String[cnt];
            v.copyInto(cmd);
        }
        return cmd;        
    }
 The tokenize() method is a wrapper for the StringTokenizer class. The StringTokenizer constructor takes two String arguments: one for the input and one for the delimiter. The junior developer incorrectly inferred that the delimiter parameter would be treated as a group of characters, not a set of single characters. I don't think that's such a poor assumption. With thousands of classes in the Java APIs, the burden of design simplicity rests on the designer's shoulders, not the application developer's. It is reasonable to assume that a String would be treated as a single group. After all, a String commonly represents a related grouping of characters.
A more correct StringTokenizer constructor would require the developer to provide an array of characters, which would clarify the fact that the delimiters for the current implementation of StringTokenizer are only single characters -- though you can specify more than one. This particular API designer was more concerned with his implementation's rapid development than its intuitiveness.
To fix the problem, we create two new static tokenize() methods: one that takes an array of characters as delimiters, one that accepts a Boolean flag to signify whether the String delimiter should be regarded as a single group. Here is the code for those two methods:

    // String tokenizer with current behavior
    public static String [] tokenize(String input, char [] delimiters)
    {
        return tokenize(input, new String(delimiters), false);
    }
    public static String [] tokenize(String input, String delimiters,
               boolean delimiterAsGroup)
    {
        Vector v = new Vector();
        String toks[] = null;
        if (!delimiterAsGroup)
        {
            StringTokenizer t = new StringTokenizer(input, delimiters);
            while (t.hasMoreTokens())
                v.addElement(t.nextToken());
        }
        else
        {
            int start = 0;
            int end = input.length();
            while (start < end)
            {
                    int delimIdx = input.indexOf(delimiters,start);
                    if (delimIdx < 0)
                    {
                            String tok = input.substring(start);
                            v.addElement(tok);
                            start = end;
                    }
                    else
                    {
                            String tok = input.substring(start, delimIdx);
                            v.addElement(tok);
                            start = delimIdx + delimiters.length();
                    }
            }
        }
        int cnt = v.size();
        if (cnt > 0)
        {
            toks = new String[cnt];
            v.copyInto(toks);
        }
       
        return toks;
    }
 Below is an applet demonstrating the new static method, tokenize(), that treats the token String ### as a single delimiter.
input: 123###4#5###678###hello###wo#rld###9
delim: ###
If '###' treated as a group delimiter expecting 6 tokens...
tok[0]: 123
tok[1]: 4#5
tok[2]: 678
tok[3]: hello
tok[4]: wo#rld
tok[5]: 9
# of tokens: 6
While some may consider the above pitfall relatively harmless, the next is extremely dangerous and should be seriously considered in any Java development project.