Letters to the Editor (June 23, 2000)

JDOM Introduction

‘Easy Java/XML Integration with JDOM, Part 1’

Jason Hunter and Brett McLaughlin

LETTER_HEAD: An alternative to calling instanceof

Jason,

From what I read in your article, JDOM is very nice. However, I have a small gripe with this:

  List mixedContent = table.getMixedContent();
  Iterator i = mixedContent.iterator();
  while (i.hasNext()) {
    Object o = i.next();
    if (o instanceof Comment) {
      // Comment has a toString()
      out.println("Comment: " + o);
    }
    else if (o instanceof String) {
      out.println("String: " + o);
    }
    else if (o instanceof ProcessingInstruction) {
      out.println("PI: " + ((ProcessingInstriction)o).getTarget());
    }
    else if (o instanceof Element) {
      out.println("Element: " + ((Element)o).getName());
    }
  }

Having to call instanceof is ugly (sorry). I think it would be better to let a user-defined object factory create an “element-handler” return in the List so there would be no need to call instanceof.

The new code would look like this:

  List mixedContent = table.getMixedContent(        myFactory      );
  Iterator i = mixedContent.iterator();
  while (i.hasNext()) {
    MyObject o = (MyObject) i.next();
    MyObject o.print();
  }

Inside getMixedContent(), it delegates the object creation to the factory. The factory object is given the Element value, and it creates the object in the way the app defines.

If the object type that getMixedContent() can return is known and not open-ended, the factory interface can look like this:

interface HandlerFactory {
    Object createCommentHandler(Comment value);
    Object createStringHandler(String value);
    Object createProcessingInstructionHandler(ProcessingInstruction value);
    Object createElementHandler(Element value);
        // etc
}

The application implements and helps getMixedContent(). instanceof does not appear anywhere.

But if you would like to keep using instanceof, just don’t pass the factory object and the List will contain Element as it does now.

Does that fit in the overall scheme?

Matt Young,

AUTHOR_REPLY:

Matt,

Thanks for your thoughts. However, people rarely have to iterate themselves over mixed content, so adding a whole handler-factory class to make that rare case slightly more elegant is unjustified. Also, I think the factory approach would be confusing to a novice programmer. In addition, the factory would execute more slowly because of the additional method calls.

Jason Hunter

:END_REPLY XML Extra

‘Programming XML in Java’

Mark Johnson

Part 1:

Part 2:

LETTER_HEAD: Any standard techniques available for producing specialized DOMs?

Mark,

I have read with interest your articles on XML in Java. They are very well written and of a high technical standard. I wait anxiously for your next article(s) on the DOM.

As a system development consultant, I try to maintain a hands-on experience with many new technologies so that I can evaluate the risk of any proposals made by developers. I have looked at a number of DOM-based developments and have seen a major inefficiency in those programs. The DOM-based developments available now use standard techniques to produce standard DOMs. Those programs then create specialized DOMs node by node so that their nodes can be used directly, for example, in graphics presentations. It seems to me that the specialized-DOM production should use a factory approach to directly produce the specialized nodes by implementing a standard technique (parser).

I have searched the Internet and have not found any references to the use of such techniques. Are they feasible, not required, or not an acceptable approach?

David Goudie

AUTHOR_REPLY:

David,

You are absolutely right and, in fact, the DOM API actually does handle what you’re referring to but in a way that’s not immediately obvious. My next XML article addresses your concern: how to use a DOM parser to create trees of application-defined classes that implement the DOM API.

If you look at the API for org.w3c.dom.Document, you’ll see:

 Attr createAttribute(java.lang.String name)
          Creates an Attr of the given name.
 CDATASection createCDATASection(java.lang.String data)
          Creates a CDATASection node whose value is the specified string.
 Comment createComment(java.lang.String data)
          Creates a Comment node given the specified string.
 DocumentFragment createDocumentFragment()
          Creates an empty DocumentFragment object.
 Element createElement(java.lang.String tagName)
          Creates an element of the type specified.
 EntityReference createEntityReference(java.lang.String name)
          Creates an EntityReference object.
 ProcessingInstruction createProcessingInstruction(java.lang.String target,
java.lang.String data)
          Creates a ProcessingInstruction node given the specified name and
data strings.
 Text createTextNode(java.lang.String data)
          Creates a Text node given the specified string.

You can create your own document class either from scratch or by subdividing an existing document-implementing class, and implement custom application objects. For example:

public Element createElement(String tagName) {
    if (tagName.equals("FRED")) {
       return new MyFredElement();
    } else if (tagName.equals("ETHEL")) {
        return new MyEthelElement();
    }
    return super.createElement(tagName);
    // Or maybe throw an exception here, or maybe use reflection to
    // find the class to create, or...
}

Where:

  1. MyFredElement and MyEthelElement are my application classes
  2. They both implement org.w3c.dom.Element

The parser defers element creation and the like to those defined methods and redefines them. You can return any subclass — assuming that each subclass implements the appropriate intrace. I’ve used that trick, and it works well.

I hope that is helpful to you.

Mark Johnson

:END_REPLY

LETTER_HEAD:

Mark:

Your XML articles are very good indeed. Most of the articles/tutorials talk about parsing XML but not about generating XML responses. Can you please send me some pointers on generating XML responses back to the client?

TIA

AUTHOR_REPLY:

TIA,

The easiest way to send an XML response from a server to a client:

System.out.println("<YourResponseTag>" + yourData + "</YourResponseTag>");

A more involved and probably more flexible approach is to build a DOM tree in memory and then print the DOM tree as XML text. Or you can walk the structure of objects you have in memory, writing (or building) XML as you go. But response XML is simply a string that your server returns to your client — at least, in the case of http. So just build that string and send it to the client.

Mark Johnson :END_REPLY

Java Q&A

‘Does Java Pass by Reference or Pass by Value?’

Tony Sintes

LETTER_HEAD: Is it impossible to write a swap method?

Tony,

I just read your article entitled “Does Java Pass by Reference or Pass by Value?”

You make it sound as if it is impossible to write a swap method, but doesn’t the following work?

File: ReferenceTest.java
import java.awt.*;
public class ReferenceTest {
static Point[] swap(Point points[]) {
Point tmp = points[0];
points[0] = points[1];
points[1] = tmp;
return points;
}
public static final void main(String args[]) {
Point points[] = new Point[2];
points[0] = new Point(20, 25);
points[1] = new Point(40, 45);
System.out.println("x0: " + points[0].x + ", y0: " + points[0].y);
System.out.println("x1: " + points[1].x + ", y1: " + points[1].y);
System.out.println("swapping...");
points = swap(points);
System.out.println("x0: " + points[0].x + ", y0: " + points[0].y);
System.out.println("x1: " + points[1].x + ", y1: " + points[1].y);
System.out.println("done.");
}
}

Rob

AUTHOR_REPLY:

Rob,

What you wrote works, as it should. However, it is not the kind of swap that I was referring to.

Let’s look at your swap and compare it to my swap:

           static Point[] swap(Point points[]) {
                      Point tmp = points[0];
                      points[0] = points[1];
                      points[1] = tmp;
                      return points;
           }

Of course, you can swap around array elements; we would be in a lot of trouble if Java didn’t allow that.

This is the type of swap I had in mind in writing my article:

           void swap(Point [] p1, Point [] p2)
           {
                      Point [] tmp = p1;
                      p1 = p2;
                      p2 = tmp
           }

Because the references are passed by copy, that swap fails. Thus, even if you swap the references, you lose the swap when the function returns. Your function works because, while the reference to the array is passed by copy, the reference still points to the same Point array. That’s why you could swap or write something such as points[0].x = 1; and make it work. Though the contents are fair game, you could not swap the array reference itself.

Tony Sintes

:END_REPLY

Tips ‘N Tricks

‘Java Tip 16: Creating In-Memory Images in Java ‘

John Zukowski

LETTER_HEAD: How do you create and display a memory image within an application?

John,

I am new to Java Graphics. I have learned a lot from your tip “Creating In-Memory Images in Java,” which works fine for an applet. What do I have to change in your source code so that it may work in an independent application as well?

Julio Silva

AUTHOR_REPLY:

Julio,

Since the article first appeared, Sun has added the BufferedImage class to the core classes.

See for an example of its usage.

John Zukowski

:END_REPLY

Tips ‘N Tricks

‘Java Tip 47: URL Authentication Revisited’

John Zukowski

LETTER_HEAD: How do you apply Tip 47 to an applet within a Webpage?

John,

We are trying to implement Tip 47 in an applet in order to prevent users from manually filling in username/password for protected pages.

  <<pass.html>>  <<passapplet.java>>

That tip works fine as a Java application, but we have problems applying it to an applet within a Webpage. In fact, the uc.setRequestProperty ("Authorization", "Basic " + encoding); does not seem to be taken into account.

When we use this.getAppletContext().showDocument(thisUrl);, the box for login/password appears.

Can you tell us what to do?

Jiang

AUTHOR_REPLY:

Jiang,

Setting the request property only works for URL connections. Using getAppletContext().showDocument() tells the browser to show the document outside of the running applet. The browser decides how to display that information. Since it won’t be using any URLConnection instances, you cannot set up the connection to implement the necessary authorization.

Either use a servlet as a proxy to obtain the information or open connection to the URL directly from the applet. Then display the HTML received directly within the applet or rely on JavaScript to take the returned document and display it within the browser. See to use JavaScript for displaying the HTML.

John Zukowski

:END_REPLY

Tips ‘N Tricks

‘Java Tip 60: Saving Bitmap Files in Java’

Jean Pierre Dubé

LETTER_HEAD:

Jean Pierre,

I have a question about “Java Tip 60: Saving Bitmap Files in Java.” Is the generated file by the application a standard bitmap file? I can’t read the generated .bmp file in, for example, Microsoft PowerPoint or Oracle Reports. Have you some explanation?

Ricardo Santos

AUTHOR_REPLY:

Ricardo,

I’m sorry about the bug. There was a problem when the size of an image ended exactly on a 4 byte boundary. Here is the corrected method writeBitmap():

   private void writeBitmap () {
      int size;
      int value;
      int j;
      int i;
      int rowCount;
      int rowIndex;
      int lastRowIndex;
      int pad;
      int padCount;
      byte rgb [] = new byte [3];
      size = (biWidth * biHeight) - 1;
      pad = 4 - ((biWidth * 3) % 4);
      if (pad == 4)   // <==== Bug correction
         pad = 0;     // <==== Bug correction
      rowCount = 1;
      padCount = 0;
      rowIndex = size - biWidth;
      lastRowIndex = rowIndex;
      try {
         for (j = 0; j < size; j++) {
            value = bitmap [rowIndex];
            rgb [0] = (byte) (value & 0xFF);
            rgb [1] = (byte) ((value >> 8) & 0xFF);
            rgb [2] = (byte) ((value >> 16) & 0xFF);
            fo.write (rgb);
            if (rowCount == biWidth) {
               padCount += pad;
               for (i = 1; i <= pad; i++) {
                  fo.write (0x00);
               }
               rowCount = 1;
               rowIndex = lastRowIndex - biWidth;
               lastRowIndex = rowIndex;
            }
            else
               rowCount++;
            rowIndex++;
         }
         //--- Update the size of the file
         bfSize += padCount - pad;
         biSizeImage += padCount - pad;
      }
      catch (Exception wb) {
         wb.printStackTrace ();
      }
   }

Try it and let me know how it went.

Jean Pierre Dubé

:END_REPLY

Source: www.infoworld.com