harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mloe...@apache.org
Subject svn commit: r410036 - in /incubator/harmony/enhanced/classlib/trunk/modules/luni/src: main/java/java/util/Formatter.java test/java/tests/api/java/util/FormatterTest.java
Date Mon, 29 May 2006 06:56:33 GMT
Author: mloenko
Date: Sun May 28 23:56:32 2006
New Revision: 410036

URL: http://svn.apache.org/viewvc?rev=410036&view=rev
Log:
fixes for HARMONY-455
java.util.Formatter.format() is not implemented

Modified:
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Formatter.java
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/FormatterTest.java

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Formatter.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Formatter.java?rev=410036&r1=410035&r2=410036&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Formatter.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Formatter.java Sun May 28 23:56:32 2006
@@ -25,7 +25,10 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
+import java.nio.CharBuffer;
 import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
 import org.apache.harmony.luni.util.NotYetImplementedException;
 
@@ -416,8 +419,7 @@
      * Flushes the formatter. If the output destination is flushable, then the
      * method flush() will be called on that destination.
      * 
-     * @throws FormatterClosedException
-     *             If the formatter has been closed.
+     * @throws FormatterClosedException If the formatter has been closed.
      */
     public void flush() {
         checkClosed();
@@ -507,8 +509,67 @@
 	 *             If the formatter has been closed.
 	 */
 	public Formatter format(Locale l, String format, Object... args) {
-		throw new NotYetImplementedException();
-	}
+        checkClosed();
+        CharBuffer formatBuffer = CharBuffer.wrap(format);
+        ParserStateMachine parser = new ParserStateMachine(formatBuffer);
+        Transformer transformer = new Transformer(this, l);
+
+        int currentObjectIndex = 0;
+        Object lastArgument = null;
+        boolean hasLastArgumentSet = false;
+        while (formatBuffer.hasRemaining()) {
+            parser.reset();
+            FormatToken token = parser.getNextFormatToken();
+            String result;
+            String plainText = token.getPlainText();
+            if (token.getConversionType() == (char) FormatToken.UNSET) {
+                result = plainText;
+            } else {
+                plainText = plainText.substring(0, plainText.indexOf('%'));
+                Object argument = null;
+                if (token.requireArgument()) {
+                    int index = token.getArgIndex() == FormatToken.UNSET ? currentObjectIndex++
+                            : token.getArgIndex();
+                    argument = getArgument(args, index, token, lastArgument,
+                            hasLastArgumentSet);
+                    lastArgument = argument;
+                    hasLastArgumentSet = true;
+                }
+                result = transformer.transform(this, token, argument);
+                result = (null == result ? plainText : plainText + result);
+            }
+            // if output is made by formattable callback
+            if (null != result) {
+                try {
+                    out.append(result);
+                } catch (IOException e) {
+                    lastIOException = e;
+                }
+            }
+        }
+        return this;
+    }
+
+    private Object getArgument(Object[] args, int index, FormatToken token,
+            Object lastArgument, boolean hasLastArgumentSet) {
+        if (index == FormatToken.LAST_ARGUMENT_INDEX && !hasLastArgumentSet) {
+            throw new MissingFormatArgumentException("<");
+        }
+
+        if (null == args) {
+            return null;
+        }
+
+        if (index >= args.length) {
+            throw new MissingFormatArgumentException(token.getPlainText());
+        }
+
+        if (index == FormatToken.LAST_ARGUMENT_INDEX) {
+            return lastArgument;
+        }
+
+        return args[index];
+    }
 
 	private static void closeOutputStream(OutputStream os) {
 		if (null == os) {
@@ -522,4 +583,797 @@
 		}
 	}
 
+	/*
+     * Information about the format string of a specified argument, which
+     * includes the conversion type, flags, width, precision and the argument
+     * index as well as the plainText that contains the whole format string used
+     * as the result for output if necessary. Besides, the string for flags is
+     * recorded to construct corresponding FormatExceptions if neccessary.
+     */
+    private static class FormatToken {
+
+        static final int LAST_ARGUMENT_INDEX = -2;
+
+        static final int UNSET = -1;
+
+        static final int FLAG_MINUS = 1;
+
+        static final int FLAG_SHARP = 1 << 1;
+
+        static final int FLAG_ADD = 1 << 2;
+
+        static final int FLAG_SPACE = 1 << 3;
+
+        static final int FLAG_ZERO = 1 << 4;
+
+        static final int FLAG_COMMA = 1 << 5;
+
+        static final int FLAG_PARENTHESIS = 1 << 6;
+
+        private static final int FLAGT_TYPE_COUNT = 6;
+
+        private int formatStringStartIndex;
+
+        private String plainText;
+
+        private int argIndex = UNSET;
+
+        private int flags = 0;
+
+        private int width = UNSET;
+
+        private int precision = UNSET;
+
+        private StringBuilder strFlags = new StringBuilder(FLAGT_TYPE_COUNT);
+
+        private char dateSuffix;// will be used in new feature.
+
+        private char conversionType = (char) UNSET;
+
+        boolean isFlagSet(int flag) {
+            return 0 != (flags & flag);
+        }
+
+        int getArgIndex() {
+            return argIndex;
+        }
+
+        void setArgIndex(int index) {
+            argIndex = index;
+        }
+
+        String getPlainText() {
+            return plainText;
+        }
+
+        void setPlainText(String plainText) {
+            this.plainText = plainText;
+        }
+
+        int getWidth() {
+            return width;
+        }
+
+        void setWidth(int width) {
+            this.width = width;
+        }
+
+        int getPrecision() {
+            return precision;
+        }
+
+        void setPrecision(int precise) {
+            this.precision = precise;
+        }
+
+        String getStrFlags() {
+            return strFlags.toString();
+        }
+
+        int getFlags() {
+            return flags;
+        }
+
+        /*
+         * Sets qualified char as one of the flags. If the char is qualified,
+         * sets it as a flag and returns true. Or else returns false.
+         */
+        boolean setFlag(char c) {
+            int newFlag;
+            switch (c) {
+                case '-': {
+                    newFlag = FLAG_MINUS;
+                    break;
+                }
+                case '#': {
+                    newFlag = FLAG_SHARP;
+                    break;
+                }
+                case '+': {
+                    newFlag = FLAG_ADD;
+                    break;
+                }
+                case ' ': {
+                    newFlag = FLAG_SPACE;
+                    break;
+                }
+                case '0': {
+                    newFlag = FLAG_ZERO;
+                    break;
+                }
+                case ',': {
+                    newFlag = FLAG_COMMA;
+                    break;
+                }
+                case '(': {
+                    newFlag = FLAG_PARENTHESIS;
+                    break;
+                }
+                default:
+                    return false;
+            }
+            if (0 != (flags & newFlag)) {
+                throw new DuplicateFormatFlagsException(String.valueOf(c));
+            }
+            flags = (flags | newFlag);
+            strFlags.append(c);
+            return true;
+
+        }
+
+        int getFormatStringStartIndex() {
+            return formatStringStartIndex;
+        }
+
+        void setFormatStringStartIndex(int index) {
+            formatStringStartIndex = index;
+        }
+
+        char getConversionType() {
+            return conversionType;
+        }
+
+        void setConversionType(char c) {
+            conversionType = c;
+        }
+
+        char getDateSuffix() {
+            return dateSuffix;
+        }
+
+        void setDateSuffix(char c) {
+            dateSuffix = c;
+        }
+
+        boolean requireArgument() {
+            return conversionType != '%' && conversionType != 'n';
+        }
+    }
+
+    /*
+     * Transforms the argument to the formatted string according to the format
+     * information contained in the format token.
+     */
+    private static class Transformer {
+
+        private Formatter formatter;
+
+        private FormatToken formatToken;
+
+        private Object arg;
+
+        private Locale locale; // will be used in new feature.
+
+        private static String lineSeparator;
+
+        Transformer(Formatter formatter, Locale locale) {
+            this.formatter = formatter;
+            this.locale = (null == locale ? Locale.US : locale);
+        }
+
+        /*
+         * Gets the formatted string according to the format token and the
+         * argument.
+         */
+        String transform(Formatter formatter, FormatToken formatToken,
+                Object arg) {
+
+            /* init data member to print */
+            this.formatToken = formatToken;
+            this.arg = arg;
+
+            String result;
+            switch (formatToken.getConversionType()) {
+                case 'B':
+                case 'b': {
+                    result = transformFromBoolean();
+                    break;
+                }
+                case 'H':
+                case 'h': {
+                    result = transformFromHashCode();
+                    break;
+                }
+                case 'S':
+                case 's': {
+                    result = transformFromString();
+                    break;
+                }
+                case 'C':
+                case 'c': {
+                    result = transformFromCharacter();
+                    break;
+                }
+                case 'd':
+                case 'o':
+                case 'x':
+                case 'X': {
+                    result = transformFromInteger();
+                    break;
+                }
+                case 'e':
+                case 'E':
+                case 'g':
+                case 'G':
+                case 'f':
+                case 'a':
+                case 'A': {
+                    result = transformFromFloat();
+                    break;
+                }
+                case '%': {
+                    result = transformFromPercent();
+                    break;
+                }
+                case 'n': {
+                    result = transfromFromLineSeparator();
+                    break;
+                }
+                case 't':
+                case 'T': {
+                    result = transformFromDateTime();
+                    break;
+                }
+                default: {
+                    throw new UnknownFormatConversionException(String
+                            .valueOf(formatToken.getConversionType()));
+                }
+            }
+
+            if (Character.isUpperCase(formatToken.getConversionType())) {
+                if (null != result) {
+                    result = result.toUpperCase(Locale.US);
+                }
+            }
+            return result;
+        }
+
+        /*
+         * Transforms the Boolean argument to a formatted string.
+         */
+        private String transformFromBoolean() {
+            StringBuilder result = new StringBuilder();
+            int startIndex = 0;
+            int flags = formatToken.getFlags();
+
+            if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
+                    && FormatToken.UNSET == formatToken.getWidth()) {
+                throw new MissingFormatWidthException("-"
+                        + formatToken.getConversionType());
+            }
+
+            // only '-' is valid for flags
+            if (0 != flags && FormatToken.FLAG_MINUS != flags) {
+                throw new FormatFlagsConversionMismatchException(formatToken
+                        .getStrFlags(), formatToken.getConversionType());
+            }
+
+            if (null == arg) {
+                result.append("false");
+            } else if (arg instanceof Boolean) {
+                result.append(arg);
+            } else {
+                result.append("true");
+            }
+            return padding(result, startIndex);
+        }
+
+        /*
+         * Transforms the hashcode of the argument to a formatted string.
+         */
+        private String transformFromHashCode() {
+            StringBuilder result = new StringBuilder();
+
+            int startIndex = 0;
+            int flags = formatToken.getFlags();
+
+            if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
+                    && FormatToken.UNSET == formatToken.getWidth()) {
+                throw new MissingFormatWidthException("-"
+                        + formatToken.getConversionType());
+            }
+
+            // only '-' is valid for flags
+            if (0 != flags && FormatToken.FLAG_MINUS != flags) {
+                throw new FormatFlagsConversionMismatchException(formatToken
+                        .getStrFlags(), formatToken.getConversionType());
+            }
+
+            if (null == arg) {
+                result.append("null");
+            } else {
+                result.append(Integer.toHexString(arg.hashCode()));
+            }
+            return padding(result, startIndex);
+        }
+
+        /*
+         * Transforms the String to a formatted string.
+         */
+        private String transformFromString() {
+            StringBuilder result = new StringBuilder();
+            int startIndex = 0;
+            int flags = formatToken.getFlags();
+
+            if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
+                    && FormatToken.UNSET == formatToken.getWidth()) {
+                throw new MissingFormatWidthException("-"
+                        + formatToken.getConversionType());
+            }
+
+            if (arg instanceof Formattable) {
+                int flag = 0;
+                // only minus and sharp flag is valid
+                if (0 != (flags & ~FormatToken.FLAG_MINUS & ~FormatToken.FLAG_SHARP)) {
+                    throw new IllegalFormatFlagsException(formatToken
+                            .getStrFlags());
+                }
+                if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)) {
+                    flag |= FormattableFlags.LEFT_JUSTIFY;
+                }
+                if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)) {
+                    flag |= FormattableFlags.ALTERNATE;
+                }
+                if (Character.isUpperCase(formatToken.getConversionType())) {
+                    flag |= FormattableFlags.UPPERCASE;
+                }
+                ((Formattable) arg).formatTo(formatter, flag, formatToken
+                        .getWidth(), formatToken.getPrecision());
+                // all actions have been taken out in the
+                // Formattable.formatTo, thus there is nothing to do, just
+                // returns null, which tells the Parser to add nothing to the
+                // output.
+                return null;
+            } else {
+                // only '-' is valid for flags if the argument is not an
+                // instance of Formattable
+                if (0 != flags && FormatToken.FLAG_MINUS != flags) {
+                    throw new FormatFlagsConversionMismatchException(
+                            formatToken.getStrFlags(), formatToken
+                                    .getConversionType());
+                }
+
+                result.append(arg);
+            }
+            return padding(result, startIndex);
+        }
+
+        /*
+         * Transforms the Character to a formatted string.
+         */
+        private String transformFromCharacter() {
+            StringBuilder result = new StringBuilder();
+
+            int startIndex = 0;
+            int flags = formatToken.getFlags();
+
+            if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
+                    && FormatToken.UNSET == formatToken.getWidth()) {
+                throw new MissingFormatWidthException("-"
+                        + formatToken.getConversionType());
+            }
+
+            // only '-' is valid for flags
+            if (0 != flags && FormatToken.FLAG_MINUS != flags) {
+                throw new FormatFlagsConversionMismatchException(formatToken
+                        .getStrFlags().toString(), formatToken
+                        .getConversionType());
+            }
+
+            if (FormatToken.UNSET != formatToken.getPrecision()) {
+                throw new IllegalFormatPrecisionException(formatToken
+                        .getPrecision());
+            }
+
+            if (null == arg) {
+                result.append("null");
+            } else {
+                if (arg instanceof Character) {
+                    result.append(arg);
+                } else if (arg instanceof Byte) {
+                    byte b = ((Byte) arg).byteValue();
+                    result.append((char) b);
+                } else if (arg instanceof Short) {
+                    short s = ((Short) arg).shortValue();
+                    result.append((char) s);
+                } else if (arg instanceof Integer) {
+                    int codePoint = ((Integer) arg).intValue();
+                    try {
+                        result.append(String.valueOf(Character
+                                .toChars(codePoint)));
+                    } catch (IllegalArgumentException e) {
+                        throw new IllegalFormatCodePointException(codePoint);
+                    }
+                } else {
+                    // argument of other class is not acceptable.
+                    throw new IllegalFormatConversionException(formatToken
+                            .getConversionType(), arg.getClass());
+                }
+            }
+            return padding(result, startIndex);
+        }
+
+        /*
+         * Transforms percent to a formatted string. Only '-' is legal flag.
+         * Precision is illegal.
+         */
+        private String transformFromPercent() {
+            StringBuilder result = new StringBuilder("%");
+
+            int startIndex = 0;
+            int flags = formatToken.getFlags();
+
+            if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
+                    && FormatToken.UNSET == formatToken.getWidth()) {
+                throw new MissingFormatWidthException("-"
+                        + formatToken.getConversionType());
+            }
+
+            if (0 != flags && FormatToken.FLAG_MINUS != flags) {
+                throw new FormatFlagsConversionMismatchException(formatToken
+                        .getStrFlags(), formatToken.getConversionType());
+            }
+            if (FormatToken.UNSET != formatToken.getPrecision()) {
+                throw new IllegalFormatPrecisionException(formatToken
+                        .getPrecision());
+            }
+            return padding(result, startIndex);
+        }
+
+        /*
+         * Transforms line separator to a formatted string. Any flag, the width
+         * or the precision is illegal.
+         */
+        private String transfromFromLineSeparator() {
+            if (FormatToken.UNSET != formatToken.getPrecision()) {
+                throw new IllegalFormatPrecisionException(formatToken
+                        .getPrecision());
+            }
+
+            if (FormatToken.UNSET != formatToken.getWidth()) {
+                throw new IllegalFormatWidthException(formatToken.getWidth());
+            }
+
+            int flags = formatToken.getFlags();
+            if (0 != flags) {
+                throw new IllegalFormatFlagsException(formatToken.getStrFlags()
+                        .toString());
+            }
+
+            if (null == lineSeparator) {
+                lineSeparator = AccessController
+                        .doPrivileged(new PrivilegedAction<String>() {
+
+                            public String run() {
+                                return System.getProperty("line.separator");
+                            }
+                        });
+            }
+            return lineSeparator;
+        }
+
+        /*
+         * Pads characters to the formatted string.
+         */
+        private String padding(StringBuilder source, int startIndex) {
+            boolean paddingRight = formatToken
+                    .isFlagSet(FormatToken.FLAG_MINUS);
+            char paddingChar = '\u0020';// space as padding char.
+            int width = formatToken.getWidth();
+            int precision = formatToken.getPrecision();
+
+            int length = source.length();
+            if (precision >= 0) {
+                length = Math.min(length, precision);
+                source = source.delete(length, source.length());
+            }
+            if (width > 0) {
+                width = Math.max(source.length(), width);
+            }
+            if (length >= width) {
+                return source.toString();
+            }
+
+            char[] paddings = new char[width - length];
+            Arrays.fill(paddings, paddingChar);
+            String insertString = new String(paddings);
+
+            if (paddingRight) {
+                source.append(insertString);
+            } else {
+                source.insert(startIndex, insertString);
+            }
+            return source.toString();
+        }
+
+        /*
+         * Transforms the Integer to a formatted string.
+         */
+        private String transformFromInteger() {
+            throw new NotYetImplementedException();
+        }
+
+        /*
+         * Transforms a Float,Double or BigDecimal to a formatted string.
+         */
+        private String transformFromFloat() {
+            throw new NotYetImplementedException();
+        }
+
+        /*
+         * Transforms a Date to a formatted string.
+         */
+        private String transformFromDateTime() {
+            throw new NotYetImplementedException();
+
+        }
+    }
+
+    private static class ParserStateMachine {
+
+        private static final char EOS = (char) -1;
+
+        private static final int EXIT_STATE = 0;
+
+        private static final int ENTRY_STATE = 1;
+
+        private static final int START_CONVERSION_STATE = 2;
+
+        private static final int FLAGS_STATE = 3;
+
+        private static final int WIDTH_STATE = 4;
+
+        private static final int PRECISION_STATE = 5;
+
+        private static final int CONVERSION_TYPE_STATE = 6;
+
+        private static final int SUFFIX_STATE = 7;
+
+        private FormatToken token;
+
+        private int state = ENTRY_STATE;
+
+        private char currentChar = 0;
+
+        private CharBuffer format = null;
+
+        ParserStateMachine(CharBuffer format) {
+            this.format = format;
+        }
+
+        void reset() {
+            this.currentChar = (char) FormatToken.UNSET;
+            this.state = ENTRY_STATE;
+            this.token = null;
+        }
+
+        /*
+         * Gets the information about the current format token. Information is
+         * recorded in the FormatToken returned and the position of the stream
+         * for the format string will be advanced till the next format token.
+         */
+        FormatToken getNextFormatToken() {
+            token = new FormatToken();
+            token.setFormatStringStartIndex(format.position());
+
+            // FINITE AUTOMATIC MACHINE
+            while (true) {
+
+                if (ParserStateMachine.EXIT_STATE != state) {
+                    // exit state does not need to get next char
+                    currentChar = getNextFormatChar();
+                }
+
+                switch (state) {
+                    // exit state
+                    case ParserStateMachine.EXIT_STATE: {
+                        process_EXIT_STATE();
+                        return token;
+                    }
+                    // plain text state, not yet applied converter
+                    case ParserStateMachine.ENTRY_STATE: {
+                        process_ENTRY_STATE();
+                        break;
+                    }
+                    // begins converted string
+                    case ParserStateMachine.START_CONVERSION_STATE: {
+                        process_START_CONVERSION_STATE();
+                        break;
+                    }
+                    case ParserStateMachine.FLAGS_STATE: {
+                        process_FlAGS_STATE();
+                        break;
+                    }
+                    case ParserStateMachine.WIDTH_STATE: {
+                        process_WIDTH_STATE();
+                        break;
+                    }
+                    case ParserStateMachine.PRECISION_STATE: {
+                        process_PRECISION_STATE();
+                        break;
+                    }
+                    case ParserStateMachine.CONVERSION_TYPE_STATE: {
+                        process_CONVERSION_TYPE_STATE();
+                        break;
+                    }
+                    case ParserStateMachine.SUFFIX_STATE: {
+                        process_SUFFIX_STATE();
+                        break;
+                    }
+                }
+            }
+        }
+
+        /*
+         * Gets next char from the format string.
+         */
+        private char getNextFormatChar() {
+            if (format.hasRemaining()) {
+                return format.get();
+            }
+            return EOS;
+        }
+
+        private String getFormatString() {
+            int end = format.position();
+            format.rewind();
+            String formatString = format.subSequence(
+                    token.getFormatStringStartIndex(), end).toString();
+            format.position(end);
+            return formatString;
+        }
+
+        private void process_ENTRY_STATE() {
+            if (EOS == currentChar) {
+                state = ParserStateMachine.EXIT_STATE;
+            } else if ('%' == currentChar) {
+                // change to conversion type state
+                state = START_CONVERSION_STATE;
+            }
+            // else remains in ENTRY_STATE
+        }
+
+        private void process_START_CONVERSION_STATE() {
+            if (Character.isDigit(currentChar)) {
+                int position = format.position() - 1;
+                int number = parseInt(format);
+                char nextChar = 0;
+                if (format.hasRemaining()) {
+                    nextChar = format.get();
+                }
+                if ('$' == nextChar) {
+                    // the digital sequence stands for the argument
+                    // index.
+                    int argIndex = number;
+                    // k$ stands for the argument whose index is k-1 except that
+                    // 0$ and 1$ both stands for the first element.
+                    if (argIndex > 0) {
+                        token.setArgIndex(argIndex - 1);
+                    } else if (argIndex == FormatToken.UNSET) {
+                        throw new MissingFormatArgumentException(
+                                getFormatString());
+                    }
+                    state = FLAGS_STATE;
+                } else {
+                    // the digital zero stands for one format flag.
+                    if ('0' == currentChar) {
+                        state = FLAGS_STATE;
+                        format.position(position);
+                    } else {
+                        // the digital sequence stands for the width.
+                        state = WIDTH_STATE;
+                        // do not get the next char.
+                        format.position(format.position() - 1);
+                        token.setWidth(number);
+                    }
+                }
+                currentChar = nextChar;
+            } else if ('<' == currentChar) {
+                state = FLAGS_STATE;
+                token.setArgIndex(FormatToken.LAST_ARGUMENT_INDEX);
+            } else {
+                state = FLAGS_STATE;
+                // do not get the next char.
+                format.position(format.position() - 1);
+            }
+
+        }
+
+        private void process_FlAGS_STATE() {
+            if (token.setFlag(currentChar)) {
+                // remains in FLAGS_STATE
+            } else if (Character.isDigit(currentChar)) {
+                token.setWidth(parseInt(format));
+                state = WIDTH_STATE;
+            } else if ('.' == currentChar) {
+                state = PRECISION_STATE;
+            } else {
+                state = CONVERSION_TYPE_STATE;
+                // do not get the next char.
+                format.position(format.position() - 1);
+            }
+        }
+
+        private void process_WIDTH_STATE() {
+            if ('.' == currentChar) {
+                state = PRECISION_STATE;
+            } else {
+                state = CONVERSION_TYPE_STATE;
+                // do not get the next char.
+                format.position(format.position() - 1);
+            }
+        }
+
+        private void process_PRECISION_STATE() {
+            if (Character.isDigit(currentChar)) {
+                token.setPrecision(parseInt(format));
+            } else {
+                // the precision is required but not given by the
+                // format string.
+                throw new UnknownFormatConversionException(getFormatString());
+            }
+            state = CONVERSION_TYPE_STATE;
+        }
+
+        private void process_CONVERSION_TYPE_STATE() {
+            token.setConversionType(currentChar);
+            if ('t' == currentChar || 'T' == currentChar) {
+                state = SUFFIX_STATE;
+            } else {
+                state = EXIT_STATE;
+            }
+
+        }
+
+        private void process_SUFFIX_STATE() {
+            token.setDateSuffix(currentChar);
+            state = EXIT_STATE;
+        }
+
+        private void process_EXIT_STATE() {
+            token.setPlainText(getFormatString());
+        }
+
+        /*
+         * Parses integer value from the given buffer
+         */
+        private int parseInt(CharBuffer format) {
+            int start = format.position() - 1;
+            int end = format.limit();
+            while (format.hasRemaining()) {
+                if (!Character.isDigit(format.get())) {
+                    end = format.position() - 1;
+                    break;
+                }
+            }
+            format.position(0);
+            String intStr = format.subSequence(start, end).toString();
+            format.position(end);
+            try {
+                return Integer.parseInt(intStr);
+            } catch (NumberFormatException e) {
+                return FormatToken.UNSET;
+            }
+        }
+    }
 }

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/FormatterTest.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/FormatterTest.java?rev=410036&r1=410035&r2=410036&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/FormatterTest.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/FormatterTest.java Sun May 28 23:56:32 2006
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FilePermission;
 import java.io.Flushable;
 import java.io.IOException;
 import java.io.PipedOutputStream;
@@ -26,9 +27,24 @@
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.security.Permission;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.DuplicateFormatFlagsException;
+import java.util.FormatFlagsConversionMismatchException;
+import java.util.Formattable;
+import java.util.FormattableFlags;
 import java.util.Formatter;
 import java.util.FormatterClosedException;
+import java.util.IllegalFormatCodePointException;
+import java.util.IllegalFormatConversionException;
+import java.util.IllegalFormatException;
+import java.util.IllegalFormatFlagsException;
+import java.util.IllegalFormatPrecisionException;
+import java.util.IllegalFormatWidthException;
 import java.util.Locale;
+import java.util.MissingFormatArgumentException;
+import java.util.MissingFormatWidthException;
+import java.util.UnknownFormatConversionException;
 import java.nio.charset.Charset;
 import junit.framework.TestCase;
 
@@ -50,14 +66,81 @@
 	}
 
 	class MockSecurityManager extends SecurityManager {
-		public void checkWrite(String filename) {
-			throw new SecurityException();
+		public void checkPermission(Permission p) {
+			if (p.getActions().equals("write") && p instanceof FilePermission) {
+				throw new SecurityException("Always throw security exception");
+			}
 		}
 
-		public void checkPermission(Permission p) {
-			if (p.getActions().equals("setSecurityManager")) {
-				return;
+		public void checkPermission(Permission p, Object ctx) {
+			checkPermission(p);
+		}
+	}
+
+	class MockFormattable implements Formattable {
+		public void formatTo(Formatter formatter, int flags, int width,
+				int precision) throws IllegalFormatException {
+			if ((flags & FormattableFlags.UPPERCASE) != 0) {
+				formatter.format("CUSTOMIZED FORMAT FUNCTION" + " WIDTH: "
+						+ width + " PRECISION: " + precision);
+			} else {
+				formatter.format("customized format function" + " width: "
+						+ width + " precision: " + precision);
+			}
+		}
+
+		public String toString() {
+			return "formattable object";
+		}
+
+		public int hashCode() {
+			return 0xf;
+		}
+	}
+
+	class MockDestination implements Appendable, Flushable {
+
+		private StringBuilder data = new StringBuilder();
+
+		private boolean enabled = false;
+
+		public Appendable append(char c) throws IOException {
+			if (enabled) {
+				data.append(c);
+				enabled = true; // enable it after the first append
+			} else {
+				throw new IOException();
+			}
+			return this;
+		}
+
+		public Appendable append(CharSequence csq) throws IOException {
+			if (enabled) {
+				data.append(csq);
+				enabled = true; // enable it after the first append
+			} else {
+				throw new IOException();
 			}
+			return this;
+		}
+
+		public Appendable append(CharSequence csq, int start, int end)
+				throws IOException {
+			if (enabled) {
+				data.append(csq, start, end);
+				enabled = true; // enable it after the first append
+			} else {
+				throw new IOException();
+			}
+			return this;
+		}
+
+		public void flush() throws IOException {
+			throw new IOException("Always throw IOException");
+		}
+
+		public String toString() {
+			return data.toString();
 		}
 	}
 
@@ -147,30 +230,26 @@
 			// expected
 		}
 
-		try {
-			f = new Formatter("notexist");
-			assertEquals(f.locale(), Locale.getDefault());
-			f.close();
-		} catch (FileNotFoundException e) {
-			fail("File can not be created");
-		}
+		f = new Formatter(notExist.getPath());
+		assertEquals(f.locale(), Locale.getDefault());
+		f.close();
 
 		f = new Formatter(fileWithContent.getPath());
 		assertEquals(0, fileWithContent.length());
-
 		f.close();
 
 		try {
 			f = new Formatter(readOnly.getPath());
-			fail("FileNotFoundException is expected");
+			fail("should throw FileNotFoundException");
 		} catch (FileNotFoundException e) {
 			// expected
 		}
 
 		SecurityManager oldsm = System.getSecurityManager();
+		System.setSecurityManager(new MockSecurityManager());
 		try {
-			System.setSecurityManager(new MockSecurityManager());
 			f = new Formatter(secret.getPath());
+			fail("should throw SecurityException");
 		} catch (SecurityException se) {
 			// expected
 		} finally {
@@ -192,18 +271,18 @@
 		}
 
 		try {
-			f = new Formatter("notexist", null);
+			f = new Formatter(notExist.getPath(), null);
 			fail("should throw NullPointerException");
 		} catch (NullPointerException e2) {
 			// expected
 		}
 
-		f = new Formatter("notexist", Charset.defaultCharset().name());
+		f = new Formatter(notExist.getPath(), Charset.defaultCharset().name());
 		assertEquals(f.locale(), Locale.getDefault());
 		f.close();
 
 		try {
-			f = new Formatter("notexist", "ISO 1111-1");
+			f = new Formatter(notExist.getPath(), "ISO 1111-1");
 			fail("should throw UnsupportedEncodingException");
 		} catch (UnsupportedEncodingException e1) {
 			// expected
@@ -221,10 +300,10 @@
 		}
 
 		SecurityManager oldsm = System.getSecurityManager();
+		System.setSecurityManager(new MockSecurityManager());
 		try {
-			System.setSecurityManager(new MockSecurityManager());
 			f = new Formatter(secret.getPath(), "UTF-16BE");
-			fail("should throw UnsupportedEncodingException");
+			fail("should throw SecurityException");
 		} catch (SecurityException se) {
 			// expected
 		} finally {
@@ -247,23 +326,24 @@
 		}
 
 		try {
-			f = new Formatter("notexist", null, Locale.KOREA);
+			f = new Formatter(notExist.getPath(), null, Locale.KOREA);
 			fail("should throw NullPointerException");
 		} catch (NullPointerException e2) {
 			// expected
 		}
 
-		f = new Formatter("notexist", Charset.defaultCharset().name(), null);
+		f = new Formatter(notExist.getPath(), Charset.defaultCharset().name(),
+				null);
 		assertNotNull(f);
 		f.close();
 
-		f = new Formatter("notexist", Charset.defaultCharset().name(),
+		f = new Formatter(notExist.getPath(), Charset.defaultCharset().name(),
 				Locale.KOREA);
 		assertEquals(f.locale(), Locale.KOREA);
 		f.close();
 
 		try {
-			f = new Formatter("notexist", "ISO 1111-1", Locale.CHINA);
+			f = new Formatter(notExist.getPath(), "ISO 1111-1", Locale.CHINA);
 			fail("should throw UnsupportedEncodingException");
 		} catch (UnsupportedEncodingException e1) {
 			// expected
@@ -272,7 +352,6 @@
 		f = new Formatter(fileWithContent.getPath(), "UTF-16BE",
 				Locale.CANADA_FRENCH);
 		assertEquals(0, fileWithContent.length());
-
 		f.close();
 
 		try {
@@ -284,8 +363,8 @@
 		}
 
 		SecurityManager oldsm = System.getSecurityManager();
+		System.setSecurityManager(new MockSecurityManager());
 		try {
-			System.setSecurityManager(new MockSecurityManager());
 			f = new Formatter(secret.getPath(),
 					Charset.defaultCharset().name(), Locale.SIMPLIFIED_CHINESE);
 			fail("should throw SecurityException");
@@ -324,8 +403,8 @@
 		}
 
 		SecurityManager oldsm = System.getSecurityManager();
+		System.setSecurityManager(new MockSecurityManager());
 		try {
-			System.setSecurityManager(new MockSecurityManager());
 			f = new Formatter(secret);
 			fail("should throw SecurityException");
 		} catch (SecurityException se) {
@@ -364,8 +443,8 @@
 		}
 
 		SecurityManager oldsm = System.getSecurityManager();
+		System.setSecurityManager(new MockSecurityManager());
 		try {
-			System.setSecurityManager(new MockSecurityManager());
 			f = new Formatter(secret, Charset.defaultCharset().name());
 			fail("should throw SecurityException");
 		} catch (SecurityException se) {
@@ -381,9 +460,9 @@
 			// expected
 		} finally {
 			if (notExist.exists()) {
-				assertTrue(notExist.delete());
 				// Fail on RI on Windows, because output stream is created and
 				// not closed when exception thrown
+				assertTrue(notExist.delete());
 			}
 		}
 
@@ -394,9 +473,9 @@
 			// expected
 		} finally {
 			if (notExist.exists()) {
-				assertTrue(notExist.delete());
 				// Fail on RI on Windows, because output stream is created and
 				// not closed when exception thrown
+				assertTrue(notExist.delete());
 			}
 		}
 	}
@@ -421,6 +500,7 @@
 		} catch (NullPointerException e2) {
 			// expected
 		}
+
 		f = new Formatter(notExist, Charset.defaultCharset().name(), null);
 		assertNotNull(f);
 		f.close();
@@ -439,7 +519,6 @@
 		f = new Formatter(fileWithContent.getPath(), "UTF-16BE",
 				Locale.CANADA_FRENCH);
 		assertEquals(0, fileWithContent.length());
-
 		f.close();
 
 		try {
@@ -451,8 +530,8 @@
 		}
 
 		SecurityManager oldsm = System.getSecurityManager();
+		System.setSecurityManager(new MockSecurityManager());
 		try {
-			System.setSecurityManager(new MockSecurityManager());
 			f = new Formatter(secret.getPath(),
 					Charset.defaultCharset().name(), Locale.SIMPLIFIED_CHINESE);
 			fail("should throw SecurityException");
@@ -634,10 +713,10 @@
 			// expected
 		}
 
-		// For destination that does not implement Flushable
 		f = new Formatter();
+		// For destination that does not implement Flushable
+		// No exception should be thrown
 		f.flush();
-		// No exception should be throw
 	}
 
 	/**
@@ -649,7 +728,23 @@
 		f.close();
 		// close next time will not throw exception
 		f.close();
-		f.ioException();
+		assertNull(f.ioException());
+	}
+
+	/**
+	 * @tests java.util.Formatter#toString()
+	 */
+	public void test_toString() {
+		Formatter f = new Formatter();
+		assertNotNull(f.toString());
+		assertEquals(f.out().toString(), f.toString());
+		f.close();
+		try {
+			f.toString();
+			fail("should throw FormatterClosedException");
+		} catch (FormatterClosedException e) {
+			// expected
+		}
 	}
 
 	/**
@@ -657,23 +752,596 @@
 	 */
 	public void test_ioException() throws IOException {
 		Formatter f = null;
-		FileOutputStream fos;
-		fos = new FileOutputStream(notExist);
-		f = new Formatter(fos);
+		f = new Formatter(new MockDestination());
 		assertNull(f.ioException());
-		fos.close();
 		f.flush();
+		assertNotNull(f.ioException());
 		f.close();
+
+		MockDestination md = new MockDestination();
+		f = new Formatter(md);
+		f.format("%s%s", "1", "2");
+		// format stop working after IOException
+		assertNotNull(f.ioException());
+		assertEquals("", f.toString());
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for null parameter
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_null() {
+		Formatter f = new Formatter();
+		try {
+			f.format((String) null, "parameter");
+			fail("should throw NullPointerException");
+		} catch (NullPointerException e) {
+			// expected
+		}
+
+		f = new Formatter();
+		f.format("hello", (Object[]) null);
+		assertEquals("hello", f.toString());
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for argument index
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_ArgIndex() {
+		Formatter formatter = new Formatter(Locale.US);
+		formatter.format("%1$s%2$s%3$s%4$s%5$s%6$s%7$s%8$s%9$s%11$s%10$s", "1",
+				"2", "3", "4", "5", "6", "7", "8", "9", "10", "11");
+		assertEquals("1234567891110", formatter.toString());
+
+		formatter = new Formatter(Locale.JAPAN);
+		formatter.format("%0$s", "hello");
+		assertEquals("hello", formatter.toString());
+
+		try {
+			formatter = new Formatter(Locale.US);
+			formatter.format("%-1$s", "1", "2");
+			fail("should throw UnknownFormatConversionException");
+		} catch (UnknownFormatConversionException e) {
+			// expected
+		}
+
+		try {
+			formatter = new Formatter(Locale.US);
+			formatter.format("%$s", "hello", "2");
+			fail("should throw UnknownFormatConversionException");
+		} catch (UnknownFormatConversionException e) {
+			// expected
+		}
+
+		formatter = new Formatter(Locale.FRANCE);
+		formatter.format("%1$s%2$s%3$s%4$s%5$s%6$s%7$s%8$s%<s%s%s%<s", "1",
+				"2", "3", "4", "5", "6", "7", "8", "9", "10", "11");
+		assertEquals("123456788122", formatter.toString());
+
+		formatter = new Formatter(Locale.FRANCE);
+		formatter.format(
+				"xx%1$s22%2$s%s%<s%5$s%<s&%7$h%2$s%8$s%<s%s%s%<ssuffix", "1",
+				"2", "3", "4", "5", "6", 7, "8", "9", "10", "11");
+		assertEquals("xx12221155&7288233suffix", formatter.toString());
+
+		try {
+			formatter.format("%<s", "hello");
+			fail("should throw MissingFormatArgumentException");
+		} catch (MissingFormatArgumentException e) {
+			// expected
+		}
+
+		formatter = new Formatter(Locale.US);
+		try {
+			formatter.format("%123$s", "hello");
+			fail("should throw MissingFormatArgumentException");
+		} catch (MissingFormatArgumentException e) {
+			// expected
+		}
+
+		formatter = new Formatter(Locale.US);
+		try {
+			// 2147483648 is the value of Integer.MAX_VALUE + 1
+			formatter.format("%2147483648$s", "hello");
+			fail("should throw MissingFormatArgumentException");
+		} catch (MissingFormatArgumentException e) {
+			// expected
+		}
+
+		try {
+			// 2147483647 is the value of Integer.MAX_VALUE
+			formatter.format("%2147483647$s", "hello");
+			fail("should throw MissingFormatArgumentException");
+		} catch (MissingFormatArgumentException e) {
+			// expected
+		}
+
+		formatter = new Formatter(Locale.US);
+		try {
+			formatter.format("%s%s", "hello");
+			fail("should throw MissingFormatArgumentException");
+		} catch (MissingFormatArgumentException e) {
+			// expected
+		}
+
+		formatter = new Formatter(Locale.US);
+		formatter.format("$100", 100);
+		assertEquals("$100", formatter.toString());
+
+		formatter = new Formatter(Locale.UK);
+		formatter.format("%01$s", "string");
+		assertEquals("string", formatter.toString());
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for width
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_Width() {
+		Formatter f = new Formatter(Locale.US);
+		f.format("%1$8s", "1");
+		assertEquals("       1", f.toString());
+
+		f = new Formatter(Locale.US);
+		f.format("%1$-1%", "string");
+		assertEquals("%", f.toString());
+
+		f = new Formatter(Locale.ITALY);
+		// 2147483648 is the value of Integer.MAX_VALUE + 1
+		f.format("%2147483648s", "string");
+		assertEquals("string", f.toString());
+
+		// the value of Integer.MAX_VALUE will allocate about 4G bytes of
+		// memory.
+		// It may cause OutOfMemoryError, so this value is not tested
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for precision
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_Precision() {
+		Formatter f = new Formatter(Locale.US);
+		f.format("%.5s", "123456");
+		assertEquals("12345", f.toString());
+
+		f = new Formatter(Locale.US);
+		// 2147483648 is the value of Integer.MAX_VALUE + 1
+		f.format("%.2147483648s", "...");
+		assertEquals("...", f.toString());
+
+		// the value of Integer.MAX_VALUE will allocate about 4G bytes of
+		// memory.
+		// It may cause OutOfMemoryError, so this value is not tested
+
+		f = new Formatter(Locale.US);
+		f.format("%10.0b", Boolean.TRUE);
+		assertEquals("          ", f.toString());
+
+		f = new Formatter(Locale.US);
+		f.format("%10.01s", "hello");
+		assertEquals("         h", f.toString());
+
+		try {
+			f = new Formatter(Locale.US);
+			f.format("%.s", "hello", "2");
+			fail("should throw UnknownFormatConversionException");
+		} catch (UnknownFormatConversionException e) {
+			// expected
+		}
+
+		try {
+			f = new Formatter(Locale.US);
+			f.format("%.-5s", "123456");
+			fail("should throw UnknownFormatConversionException");
+		} catch (UnknownFormatConversionException e) {
+			// expected
+		}
+
+		try {
+			f = new Formatter(Locale.US);
+			f.format("%1.s", "hello", "2");
+			fail("should throw UnknownFormatConversionException");
+		} catch (UnknownFormatConversionException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		f.format("%5.1s", "hello");
+		assertEquals("    h", f.toString());
+
+		f = new Formatter(Locale.FRANCE);
+		f.format("%.0s", "hello", "2");
+		assertEquals("", f.toString());
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for line sperator
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_LineSperator() {
+		Formatter f = null;
+
+		String oldSeparator = System.getProperty("line.separator");
+		System.setProperty("line.separator", "!\n");
+
+		f = new Formatter(Locale.US);
+		f.format("%1$n", 1);
+		assertEquals("!\n", f.toString());
+
+		f = new Formatter(Locale.KOREAN);
+		f.format("head%1$n%2$n", 1, new Date());
+		assertEquals("head!\n!\n", f.toString());
+
+		f = new Formatter(Locale.US);
+		f.format("%n%s", "hello");
+		assertEquals("!\nhello", f.toString());
+
+		System.setProperty("line.separator", oldSeparator);
+
+		f = new Formatter(Locale.US);
+		final String[] illFlags = { "%-n", "%+n", "%#n", "% n", "%0n", "%,n",
+				"%(n" };
+		for (int i = 0; i < illFlags.length; i++) {
+			try {
+				f.format(illFlags[i]);
+				fail("should throw IllegalFormatFlagsException: " + illFlags[i]);
+			} catch (IllegalFormatFlagsException e) {
+				// expected
+			}
+		}
+
+		f = new Formatter(Locale.US);
+		try {
+			f.format("%4n");
+			fail("should throw IllegalFormatWidthException");
+		} catch (IllegalFormatWidthException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		try {
+			f.format("%-4n");
+			fail("should throw IllegalFormatWidthException");
+		} catch (IllegalFormatWidthException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		try {
+			f.format("%.9n");
+			fail("should throw IllegalFormatPrecisionException");
+		} catch (IllegalFormatPrecisionException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		try {
+			f.format("%5.9n");
+			fail("should throw IllegalFormatPrecisionException");
+		} catch (IllegalFormatPrecisionException e) {
+			// expected
+		}
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for percent
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_Percent() {
+		Formatter f = null;
+
+		f = new Formatter(Locale.ENGLISH);
+		f.format("%1$%", 100);
+		assertEquals("%", f.toString());
+
+		f = new Formatter(Locale.CHINA);
+		f.format("%1$%%%", "hello", new Object());
+		assertEquals("%%", f.toString());
+
+		f = new Formatter(Locale.CHINA);
+		f.format("%%%s", "hello");
+		assertEquals("%hello", f.toString());
+
+		f = new Formatter(Locale.US);
+		try {
+			f.format("%.9%");
+			fail("should throw IllegalFormatPrecisionException");
+		} catch (IllegalFormatPrecisionException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		try {
+			f.format("%5.9%");
+			fail("should throw IllegalFormatPrecisionException");
+		} catch (IllegalFormatPrecisionException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		final String[] illFlags = { "%+%", "%#%", "% %", "%0%", "%,%", "%(%" };
+		for (int i = 0; i < illFlags.length; i++) {
+			try {
+				f.format(illFlags[i]);
+				fail("should throw FormatFlagsConversionMismatchException: "
+						+ illFlags[i]);
+				/*
+				 * error on RI, throw IllegalFormatFlagsException specification
+				 * says FormatFlagsConversionMismatchException should be thrown
+				 */
+			} catch (FormatFlagsConversionMismatchException e) {
+				// expected
+			}
+		}
+
+		f = new Formatter(Locale.KOREAN);
+		f.format("%4%", 1);
+		/*
+		 * fail on RI the output string should be right justified by appending
+		 * spaces till the whole string is 4 chars width.
+		 */
+		assertEquals("   %", f.toString());
+
+		f = new Formatter(Locale.US);
+		f.format("%-4%", 100);
+		/*
+		 * fail on RI, throw UnknownFormatConversionException the output string
+		 * should be left justified by appending spaces till the whole string is
+		 * 4 chars width.
+		 */
+		assertEquals("%   ", f.toString());
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for flag
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_Flag() {
+		Formatter f = new Formatter(Locale.US);
+		try {
+			f.format("%1$-#-8s", "something");
+			fail("should throw DuplicateFormatFlagsException");
+		} catch (DuplicateFormatFlagsException e) {
+			// expected
+		}
+
+		final char[] chars = { '-', '#', '+', ' ', '0', ',', '(', '%', '<' };
+		Arrays.sort(chars);
+		f = new Formatter(Locale.US);
+		for (char i = 0; i <= 256; i++) {
+			// test 8 bit character
+			if (Arrays.binarySearch(chars, i) >= 0 || Character.isDigit(i)
+					|| Character.isLetter(i)) {
+				// Do not test 0-9, a-z, A-Z and characters in the chars array.
+				// They are characters used as flags, width or conversions
+				continue;
+			}
+			try {
+				f.format("%" + i + "s", 1);
+				fail("should throw UnknownFormatConversionException");
+			} catch (UnknownFormatConversionException e) {
+				// expected
+			}
+		}
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for general
+	 *        conversion
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_GeneralConversion() {
+		final String[] flagMismatch = { "%#b", "%+b", "% b", "%0b", "%,b",
+				"%(b", "%#B", "%+B", "% B", "%0B", "%,B", "%(B", "%#h", "%+h",
+				"% h", "%0h", "%,h", "%(h", "%#H", "%+H", "% H", "%0H", "%,H",
+				"%(H", "%+s", "% s", "%0s", "%,s", "%(s", "%+S", "% S", "%0S",
+				"%,S", "%(S" };
+
+		Formatter f = new Formatter(Locale.US);
+
+		for (int i = 0; i < flagMismatch.length; i++) {
+			try {
+				f.format(flagMismatch[i], "something");
+				fail("should throw FormatFlagsConversionMismatchException");
+			} catch (FormatFlagsConversionMismatchException e) {
+				// expected
+			}
+		}
+
+		try {
+			f.format("%-.8s", "something");
+			fail("should throw MissingFormatWidthException");
+		} catch (MissingFormatWidthException e) {
+			// expected
+		}
+
+		final Object[] args = { Boolean.FALSE, Boolean.TRUE,
+				new Character('c'), new Byte((byte) 0x01),
+				new Short((short) 0x0001), new Integer(1), new Float(1.1f),
+				new Double(1.1d), "", "string content", new MockFormattable(),
+				(Object) null };
+
+		final String[] bResult = { " fa", "false", "fa", " tr", "true", "tr",
+				" tr", "true", "tr", " tr", "true", "tr", " tr", "true", "tr",
+				" tr", "true", "tr", " tr", "true", "tr", " tr", "true", "tr",
+				" tr", "true", "tr", " tr", "true", "tr", " tr", "true", "tr",
+				" fa", "false", "fa" };
+		for (int i = 0, resultCount = 0; i < args.length; i++) {
+			f = new Formatter(Locale.JAPANESE);
+			f.format("prefix%3.2b", args[i]);
+			assertEquals("prefix" + bResult[resultCount], f.toString());
+
+			f = new Formatter(Locale.JAPANESE);
+			f.format("prefix%3.2B", args[i]);
+			assertEquals("prefix" + bResult[resultCount++].toUpperCase(), f
+					.toString());
+
+			f = new Formatter(Locale.KOREA);
+			f.format("%-4.6b", args[i]);
+			assertEquals(bResult[resultCount], f.toString());
+
+			f = new Formatter(Locale.KOREA);
+			f.format("%-4.6B", args[i]);
+			assertEquals(bResult[resultCount++].toUpperCase(), f.toString());
+
+			f = new Formatter(Locale.ITALY);
+			f.format("%.2bsuffix", args[i]);
+			assertEquals(bResult[resultCount] + "suffix", f.toString());
+
+			f = new Formatter(Locale.ITALY);
+			f.format("%.2Bsuffix", args[i]);
+			assertEquals(bResult[resultCount++].toUpperCase() + "suffix", f
+					.toString());
+		}
+
+		final String[] sResult = { "fal", "fals  ", "false", "tru", "true  ",
+				"true", " c", "c     ", "c", " 1", "1     ", "1", " 1",
+				"1     ", "1", " 1", "1     ", "1", "1.1", "1.1   ", "1.1",
+				"1.1", "1.1   ", "1.1", "  ", "      ", "", "str", "stri  ",
+				"strin", "customized format function width: 2 precision: 3",
+				"customized format function width: 6 precision: 4",
+				"customized format function width: -1 precision: 5", "nul",
+				"null  ", "null" };
+		for (int i = 0, resultCount = 0; i < args.length; i++) {
+			f = new Formatter(Locale.CHINA);
+			f.format("%2.3s", args[i]);
+			assertEquals(sResult[resultCount], f.toString());
+
+			f = new Formatter(Locale.CHINA);
+			f.format("%2.3S", args[i]);
+			assertEquals(sResult[resultCount++].toUpperCase(), f.toString());
+
+			f = new Formatter(Locale.KOREA);
+			f.format("%-6.4s", args[i]);
+			assertEquals(sResult[resultCount], f.toString());
+
+			f = new Formatter(Locale.KOREA);
+			f.format("%-6.4S", args[i]);
+			assertEquals(sResult[resultCount++].toUpperCase(), f.toString());
+
+			f = new Formatter(Locale.GERMAN);
+			f.format("%.5s", args[i]);
+			assertEquals(sResult[resultCount], f.toString());
+
+			f = new Formatter(Locale.GERMAN);
+			f.format("%.5S", args[i]);
+			assertEquals(sResult[resultCount++].toUpperCase(), f.toString());
+		}
+
+		// skip args[length-1]: null, do it seperately
+		for (int i = 0; i < args.length - 1; i++) {
+			f = new Formatter(Locale.CHINA);
+			f.format("%h", args[i]);
+			assertEquals(Integer.toHexString(args[i].hashCode()), f.toString());
+
+			f = new Formatter(Locale.CHINA);
+			f.format("%H", args[i]);
+			assertEquals(Integer.toHexString(args[i].hashCode()).toUpperCase(),
+					f.toString());
+		}
+
+		/*
+		 * In Turkish locale, the upper case of '\u0069' is '\u0130'. The
+		 * following test indicate that '\u0069' is coverted to upper case
+		 * without using the turkish locale.
+		 */
+		f = new Formatter(new Locale("tr"));
+		f.format("%S", "\u0069");
+		assertEquals("\u0049", f.toString());
+
+		f = new Formatter(Locale.GERMAN);
+		for (int i = 0; i < args.length; i++) {
+			if (!(args[i] instanceof Formattable)) {
+				try {
+					f.format("%#s", args[i]);
+					/*
+					 * fail on RI If the '#' flag is present and the argument is
+					 * not a Formattable , then a
+					 * FormatFlagsConversionMismatchException should be thrown.
+					 */
+					fail("should throw FormatFlagsConversionMismatchException");
+				} catch (FormatFlagsConversionMismatchException e) {
+					// expected
+				}
+			} else {
+				f.format("%#s%<-#8s", args[i]);
+				assertEquals(
+						"customized format function width: -1 precision: -1customized format function width: 8 precision: -1",
+						f.toString());
+			}
+		}
+	}
+
+	/**
+	 * @tests java.util.Formatter#format(String, Object...) for Character
+	 *        conversion
+	 */
+	public void test_formatLjava_lang_String$Ljava_lang_Object_CharacterConversion() {
+		Formatter f = new Formatter(Locale.US);
+		final Object[] illArgs = { Boolean.TRUE, new Float(1.1f),
+				new Double(1.1d), "string content", new Float(1.1f), new Date() };
+		for (int i = 0; i < illArgs.length; i++) {
+			try {
+				f.format("%c", illArgs[i]);
+				fail("should throw IllegalFormatConversionException");
+			} catch (IllegalFormatConversionException e) {
+				// expected
+			}
+		}
+
+		try {
+			f.format("%c", Integer.MAX_VALUE);
+			fail("should throw IllegalFormatCodePointException");
+		} catch (IllegalFormatCodePointException e) {
+			// expected
+		}
+
+		try {
+			f.format("%#c", 'c');
+			fail("should throw FormatFlagsConversionMismatchException");
+		} catch (FormatFlagsConversionMismatchException e) {
+			// expected
+		}
+
+		final Object[] legalArgs = { 'c', '\u0123', (byte) 0x11,
+				(short) 0x1111, 0x11 };
+		final Object[] result = { "c", "c ", "\u0123", "\u0123 ", "\u0011",
+				"\u0011 ", "\u1111", "\u1111 ", "\u0011", "\u0011 " };
+
+		for (int i = 0, resultCount = 0; i < legalArgs.length; i++) {
+			f = new Formatter(Locale.US);
+			f.format("pre%c", legalArgs[i]);
+			assertEquals("pre" + result[resultCount++], f.toString());
+
+			f = new Formatter(Locale.GERMANY);
+			f.format("%-2c.txt", legalArgs[i]);
+			assertEquals(result[resultCount++] + ".txt", f.toString());
+		}
+
+		f = new Formatter(Locale.US);
+		f.format("%c", 0x10000);
+		assertEquals(0x10000, f.toString().codePointAt(0));
+
+		try {
+			f.format("%2.2c", 'c');
+			fail("should throw IllegalFormatPrecisionException");
+		} catch (IllegalFormatPrecisionException e) {
+			// expected
+		}
+
+		f = new Formatter(Locale.US);
+		f.format("%C", 'w');
+		// error on RI, throw UnknownFormatConversionException
+		// RI do not support converter 'C'
+		assertEquals("W", f.toString());
+
+		f = new Formatter(Locale.JAPAN);
+		f.format("%Ced", 0x1111);
+		// error on RI, throw UnknownFormatConversionException
+		// RI do not support converter 'C'
+		assertEquals("\u1111ed", f.toString());
 	}
 
 	/**
 	 * Setup resource files for testing
 	 */
 	protected void setUp() throws IOException {
-		notExist = new File("notexist");
-		if (notExist.exists()) {
-			notExist.delete();
-		}
+		notExist = File.createTempFile("notexist", null);
+		notExist.delete();
 
 		fileWithContent = File.createTempFile("filewithcontent", null);
 		BufferedOutputStream bw = new BufferedOutputStream(



Mime
View raw message