Archive for the ‘Java’ Category

RomanNumeralFormat

Sunday, March 15th, 2009

Several years ago I wrote this RomanNumeralFormat class which extends java.text.NumberFormat but parses and formats Roman numerals. I just stumbled across it and thought it belonged here. This sort of thing should definitely be included in Java 7, then it will truly rule over Perl 6.

import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;

public class RomanNumeralFormat extends NumberFormat {

  private static char[] roman_alpha = new char[] { 'M', 'D', 'C',
    'L', 'X', 'V', 'I' };
  private static int[] roman_num = new int[] { 1000, 500, 100, 50,
    10, 5, 1 };

  public StringBuffer format(double number, StringBuffer toAppendTo,
    FieldPosition pos) {
    // Too lazy
    return null;
  }

  public StringBuffer format(long l, StringBuffer toAppendTo,
    FieldPosition pos) {
    StringBuffer buf = new StringBuffer(20);

    while (l > 0) {
      for (int i = 0; i < roman_num.length; i++) {
        if (l >= roman_num[i]) {
          buf.append(roman_alpha[i]);
          l -= roman_num[i];
          break;
        } else {
          int peek = i + 2 - (i % 2);
          if (peek < roman_num.length) {
            int j = roman_num[i] - roman_num[peek];
            if (l >= j && j > 0) {
              buf.append(roman_alpha[peek]);
              buf.append(roman_alpha[i]);
              l -= j;
              break;
            }
          }
        }
      }
    }

    return buf;
  }

  public Number parse(String source, ParsePosition parsePosition) {
    long l = 0;
    int max = 0;

    source = source.toUpperCase();

    for (int i = source.length() - 1; i >= 0; i--) {
      char c = source.charAt(i);
      boolean validChar = false;
      for (int j = 0; j < roman_alpha.length; j++) {
        if (roman_alpha[j] == c) {
          int k = roman_num[j];
          if (k >= max) {
            l += k;
            max = k;
          } else {
            l -= k;
          }
          validChar = true;
          break;
        }
      }
      if (!validChar) {
        parsePosition.setErrorIndex(i);
        return null;
      }
    }

    parsePosition.setIndex(source.length());
    return new Long(l);
  }
}

I was obviously pretty concerned about getting some large roman numerals through here – using a long rather than an int. Built to last.

AMF3 integer parsing

Saturday, May 6th, 2006

Martin Schnabel has done some work on further reverse engineering the integer format in AMF 3, and discovering the undefined type. I have updated my reference AMF 3 implementation accordingly. Including a simpler parse integer algorithm, which I’ve pasted below:


private int readAMF3Integer() throws IOException {
    int n = 0;
    int b = in.readUnsignedByte();
    int result = 0;

    while ((b & 0x80) != 0 && n < 3) {
        result <<= 7;
        result |= (b & 0x7f);
        b = in.readUnsignedByte();
        n++;
    }
    if (n < 3) {
        result <<= 7;
        result |= b;
    } else {
    	/* Use all 8 bits from the 4th byte */
    	result <<= 8;
    	result |= b;

    	/* Check if the integer should be negative */
    	if ((result & 0x10000000) != 0) {
    		/* and extend the sign bit */
    		result |= 0xe0000000;
    	}
    }

    return result;
}

AMF3 reference deserializer in Java

Friday, April 28th, 2006

I’ve released some source code into the Public Domain, from my work in adding AMF3 deserialization support to Charles, as a reference for and compliment to the AMF3 specification.

The source code is a single Java file containing all of the AMF3 specific implementation. You can download it here as a zip file: AMF3Deserializer.zip.

UPDATE 6 May 2006: The reference implementation has been updated to include the newly discovered Undefined type and to correct the integer parsing (including negative integer parsing, credit to Martin Schnabel).

UPDATE 23 December 2006: Another update released here.

Patch for JDK 1.5 ZipFile to fix slow network drive access

Saturday, April 22nd, 2006

First published 7 September 2005.

There is a problem with java.util.ZipFile in JDK 1.5 that it is very slow to perform some operations when the ZIP/JAR file is on a network drive. See this bug report.

This patch is based on the source for ZipFile from JDK 1.5.0_04. It is intended for use on Windows only (but see Limitations). The download includes source, a compiled version of the patch and a Windows .BAT file for installing it.

The installation just involves adding the patched classes into your rt.jar file for your Java runtime environment.

Patch Detail

The patched java.util.ZipFile looks at the path of the zip/jar file when trying to open it. If it determines that the path is on a network drive it instead copies it to a location on your local drive and uses that copy instead. If the copy already exists it checks the timestamps to ensure it is up to date. Copies are stored in the temporary directory on your system as determined by System.getProperty("java.io.tmpdir").

This results in a massive speed-up for accessing zips/jars on network drives, and a small cache of zips/jars on your local drive.

In the source code the patch changes are all between the /* PATCH BEGINS */ and /* PATCH ENDS */ comments. The rest is the original code.

Limitations

The patch is written with Windows in mind, as it checks for C: at the start of the full path in order to determine whether the file is on a network drive or not! This obviously won’t work very well for other operating systems. It will be really easy to change the patch to suit those requirements though. The source is included in the download; if you do this please let me know so that I can add your efforts to this page.

Installation Instructions

You can download the patch here. There is a readme file in the ZIP.

Download Windows patch + source