test/jdk/java/text/Format/common/FormatIteratorTest.java
author erikj
Tue, 12 Sep 2017 19:03:39 +0200
changeset 47216 71c04702a3d5
parent 40948 jdk/test/java/text/Format/common/FormatIteratorTest.java@5869c625afaa
permissions -rw-r--r--
8187443: Forest Consolidation: Move files to unified layout Reviewed-by: darcy, ihse

/*
 * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/**
 * @test
 * @bug 4018937
 * @library /java/text/testlib
 * @build FormatIteratorTest PParser IntlTest
 * @run main FormatIteratorTest
 * @summary Tests the formatToCharacterIterator method of SimpleDateFormat,
 *          MessageFormat and DecimalFormat.
 */

import java.io.*;
import java.lang.reflect.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.text.AttributedCharacterIterator.Attribute;

/**
 * FormatTester creates Formats, and tests the resulting FieldPositions
 * and AttributedCharacterIterator based on a file. The file is a hierarchical
 * set of key/value pairs, where each value can also be an array or map. The
 * top map must contain a tests entry, which will be an array consisting
 * of pairs of maps. The first map specifies the Format that
 * should be created, and consists of:
 * <pre>
 *   class = className
 *   args = (arg1 arg2 ...)
 *   valueClass = className
 *   valueArgs = (arg1 arg2 ...)
 * </pre>
 * The second map dictates what to test, and should consist of the following:
 * <pre>
 *   length = lengthOfFormattedString
 *   text = Result of Formatting
 *   0...lengthOfFormattedString = (arg1 arg2 ...)
 *   limits = ( range1 range2 ...)
 *   fieldPositions = ( fp1 fp2 ...)
 * </pre>
 * <code>lengthOfFormattedString</code> indicate the total length of formatted
 * string. <code>text</code> indicates the resulting string.
 * <code>0...x</code> where x == <code>lengthOfFormattedString - 1</code> is
 * an array of the attributes that should exist at the particular
 * location. <code>limits</code> is an array of maps, where each map
 * can be used to test the bounds of a set of attributes. Each map will
 * consist of:
 * <pre>
 *   attributes = array of attributes
 *   begin = start location
 *   begin2 = second start location
 *   end = limit location
 *   end2 = second limit location
 * </pre>
 * These are tested by iterating from begin to end in the CharacterIterator
 * and doing the following at each index:
 * <pre>
 *   getRunStart() == begin
 *   getRunStart(attributes) == begin2
 *   getRunLimit() == end
 *   getRunLimit(attributes) == end2
 * </pre>
 * <code>fieldPositions</code> is used to test the results of invoking
 * <code>format</code> with a <code>FieldPosition</code>.
 * <code>fieldPositions</code> is an array of maps, where each map contains
 * the following:
 * <pre>
 *   field = Integer field reference (optional)
 *   fieldID = Object reference
 *   begin = begin index of FieldPosition after formatting
 *   end = end index of FieldPosition after formatting
 * </pre>
 * Any lines starting with {@code '#'} are comment lines and ignored.
 */
public class FormatIteratorTest extends IntlTest {
    private Format format;
    private Object value;
    private String text;

    public static final Object ARG0_FIELD_ID = MessageFormat.
                                                     Field.ARGUMENT;
    public static final Object ARG1_FIELD_ID = MessageFormat.
                                                     Field.ARGUMENT;
    public static final Object ARG2_FIELD_ID = MessageFormat.
                                                     Field.ARGUMENT;
    public static final Object ARG3_FIELD_ID = MessageFormat.
                                                     Field.ARGUMENT;

    public static void main(String[] args) throws Exception {
        Locale reservedLocale = Locale.getDefault();
        TimeZone reservedTimeZone = TimeZone.getDefault();
        try {
            // The current tests are only appropriate for US. If tests are
            // added for other locales are added, then a property should be
            // added to each file (test) to be able to specify the locale.
            Locale.setDefault(Locale.US);
            TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
            new FormatIteratorTest().run(args);
        } finally {
            // restore the reserved locale and time zone
            Locale.setDefault(reservedLocale);
            TimeZone.setDefault(reservedTimeZone);
        }
    }

    public FormatIteratorTest() {
    }

    public void testDecimalFormat() {
        _test(new File(System.getProperty("test.src", "."),
                       "decimalFormat.props"));
    }

    public void testMessageFormat() {
        _test(new File(System.getProperty("test.src", "."),
                       "messageFormat.props"));
    }

    public void testDateFormat() {
        _test(new File(System.getProperty("test.src", "."),
                       "dateFormat.props"));
    }

    @SuppressWarnings("unchecked")
    private void _test(File file) {
        try {
            logln("testing: " + file);
            PParser parser = new PParser();
            Map<String,Object> contents = parser.parse(new BufferedReader(
                new FileReader(file)));
            List<Object> test = (List)contents.get("tests");

            for (int counter = 0; counter < test.size(); counter++) {
                logln("creating: " + (counter / 2));

                AttributedCharacterIterator iterator =
                    create((Map)test.get(counter));

                logln("verifying: " + (counter / 2));
                verify(iterator, (Map)test.get(++counter));
            }
        } catch (IOException ioe) {
            errln("Error reading: " + ioe);
        }
    }

    @SuppressWarnings("unchecked")
    public void verify(AttributedCharacterIterator iterator,Map<String,Object> table) {
        int length = Integer.parseInt((String)table.get("length"));

        // Verify the text
        if (!getText(iterator).equals(
                escapeIfNecessary((String)table.get("text")))) {
            String text = getText(iterator);

            errln("text doesn't match, got: " + getText(iterator));
        }
        if (iterator.getBeginIndex() != 0) {
            errln("Bogus start: " + iterator.getBeginIndex());
        }
        if (iterator.getEndIndex() != length) {
            errln("Bogus end: " + iterator.getEndIndex());
        }
        for (int counter = 0; counter < length; counter++) {
            iterator.setIndex(counter);
            if (!verifyAttributes(iterator.getAttributes().keySet(),
                    makeAttributes((List)table.get(Integer.
                                                      toString(counter))))) {
                errln("Attributes don't match at " + counter + " expecting " +
                      makeAttributes((List)table.get(Integer.toString
                                                       (counter))) + " got " +
                      iterator.getAttributes().keySet());
            }
        }
        for (int counter = length - 1; counter >= 0; counter--) {
            iterator.setIndex(counter);
            if (!verifyAttributes(iterator.getAttributes().keySet(),
                    makeAttributes((List)table.get(Integer.
                                                      toString(counter))))) {
                errln("Attributes don't match at " + counter + " expecting " +
                      makeAttributes((List)table.get(Integer.toString
                                                       (counter))) + " got " +
                      iterator.getAttributes().keySet());
            }
        }
        verifyLimits(iterator, table);

        text = escapeIfNecessary((String)table.get("text"));
        List<Object> fps = (List)table.get("fieldPositions");

        if (fps != null) {
            for (int counter = 0; counter < fps.size(); counter++) {
                verifyFieldPosition(counter,(Map)fps.get(counter));
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void verifyLimits(AttributedCharacterIterator iterator,
                              Map<String,Object> table) {
        List<Object> limits = (List)table.get("limits");

        if (limits != null) {
            for (int counter = 0; counter < limits.size(); counter++) {
                verifyLimit(iterator, (Map)limits.get(counter));
            }
        }
    }

    private void verifyLimit(AttributedCharacterIterator iterator,
                             Map<String,Object> table) {
        int begin = Integer.parseInt((String)table.get("begin"));
        int end = Integer.parseInt((String)table.get("end"));
        @SuppressWarnings("unchecked")
        Set<Attribute> attrs = makeAttributes((List)table.get("attributes"));
        String begin2S = (String)table.get("begin2");
        int begin2 = (begin2S != null) ? Integer.parseInt(begin2S) : begin;
        String end2S = (String)table.get("end2");
        int end2 = (end2S != null) ? Integer.parseInt(end2S) : end;

        for (int counter = begin; counter < end; counter++) {
            iterator.setIndex(counter);
            if (iterator.getRunStart() != begin) {
                errln("Begin doesn't match want " + begin + " got " +
                      iterator.getRunStart() + " at " + counter + " attrs " +
                      attrs);
            }
            if (iterator.getRunStart(attrs) != begin2) {
                errln("Begin2 doesn't match want " + begin2 + " got " +
                      iterator.getRunStart(attrs) + " at " + counter +
                      " attrs " + attrs);
            }
            if (iterator.getRunLimit() != end) {
                errln("End doesn't match want " + end + " got " +
                      iterator.getRunLimit() + " at " + counter + " attrs " +
                      attrs);
            }
            if (iterator.getRunLimit(attrs) != end2) {
                errln("End2 doesn't match want " + end2 + " got " +
                      iterator.getRunLimit(attrs) + " at " + counter +
                      " attrs " + attrs);
            }
        }
    }

    private boolean verifyAttributes(Set<Attribute> a, Set<Attribute> b) {
        boolean aEmpty = a.isEmpty();
        boolean bEmpty = b.isEmpty();

        if (aEmpty && bEmpty) {
            return true;
        }
        else if (aEmpty || bEmpty) {
            return false;
        }
        return a.equals(b);
    }

    private String getText(AttributedCharacterIterator iterator) {
        StringBuffer buffer = new StringBuffer();

        for (int counter = 0; counter < iterator.getEndIndex(); counter++) {
            buffer.append(iterator.setIndex(counter));
        }
        return buffer.toString();
    }

    private void verifyFieldPosition(int index, Map<String,Object> table) {
        Object o = table.get("field");
        int begin = Integer.parseInt((String)table.get("begin"));
        int end = Integer.parseInt((String)table.get("end"));

        if (o != null) {
            FieldPosition fp = new FieldPosition(((Integer)
                                          lookupField((String)o)));

            verifyFieldPosition(fp, begin, end, index);
        }
        o = table.get("fieldID");
        if (o != null) {
            FieldPosition fp = new FieldPosition((Format.Field)
                                                 lookupField((String)o));
            verifyFieldPosition(fp, begin, end, index);
        }
    }

    private void verifyFieldPosition(FieldPosition fp, int begin, int end,
                                     int index) {
        StringBuffer buffer = new StringBuffer();

        format.format(value, buffer, fp);
        if (fp.getBeginIndex() != begin) {
            errln("bogus begin want " + begin + " got " + fp.getBeginIndex() +
                  " for " + fp + " at " + index);
        }
        if (fp.getEndIndex() != end) {
            errln("bogus end want " + end + " got " + fp.getEndIndex() +
                  " for " + fp + " at " + index);
        }
        if (!buffer.toString().equals(text)) {
            errln("Text does not match, want !" + buffer.toString() +
                  "! got !" + text + "!");
        }
    }

    public AttributedCharacterIterator create(Map<String,Object> table) {
        format = (Format)createInstance((String)table.get("class"),
                                        ((List)table.get("args")).toArray());
        value = createInstance((String)table.get("valueClass"),
                               ((List)table.get("valueArgs")).toArray());

        logln("Created format: " + format + " value " + value);
        AttributedCharacterIterator aci = format.
                           formatToCharacterIterator(value);

        logln("Obtained Iterator: " + aci);
        return aci;
    }

    public Format.Field makeAttribute(String name) {
        return (Format.Field)lookupField(name);
    }

    private Object createInstance(String className, Object[] args) {
        if (className.equals("java.lang.reflect.Array")) {
            for (int counter = 0; counter < args.length; counter++) {
                if (args[counter] instanceof List) {
                    @SuppressWarnings("unchecked")
                    List<Object> v = (List<Object>)args[counter];

                    args[counter] = createInstance((String)v.get(0),
                                               ((List)v.get(1)).toArray());
                }
            }
            return args;
        }
        for (int counter = 0; counter < args.length; counter++) {
            args[counter] = escapeIfNecessary((String)args[counter]);
        }
        try {
            if (className.equals("java.util.concurrent.atomic.AtomicInteger")) {
                return new AtomicInteger(Integer.valueOf((String)args[0]));
            } else if (className.equals("java.util.concurrent.atomic.AtomicLong")) {
                return new AtomicLong(Long.valueOf((String)args[0]));
            } else {
                Class<?> klass = lookupClass(className);
                Constructor<?> cons = klass.getConstructor(
                    new Class<?>[] { String.class });
                Object value = cons.newInstance(args);

                return value;
            }
        } catch (Throwable th) {
            errln("Error creating instance " + th);
            return null;
        }
    }

    private  Class<?> lookupClass(String name) throws ClassNotFoundException {
        try {
            Class<?> klass = Class.forName(name);

            return klass;
        } catch (ClassNotFoundException e1) {}

        try {
            Class<?> klass = Class.forName("java.lang." + name);

            return klass;
        } catch (ClassNotFoundException e1) {}

        Class<?> klass = Class.forName("java.text." + name);

        return klass;
    }

    private Object lookupField(String name) {
        Throwable error = null;

        try {
            int dotIndex = name.indexOf('.');
            Class<?> klass = lookupClass(name.substring(0, dotIndex));
            String fieldName = name.substring(dotIndex + 1);
            Field[] fields = klass.getFields();

            for (int counter = fields.length - 1; counter >= 0; counter--) {
                if (fields[counter].getName().equals(fieldName)) {
                    return fields[counter].get(null);
                }
            }
        } catch (Throwable th) {
            error = th;
        }
        errln("Could not lookup field " + name + " " + error);
        return null;
    }

    protected String escapeIfNecessary(String string) {
        if (string != null) {
            int index;

            if ((index = string.indexOf("\\u")) != -1) {
                StringBuffer sb = new StringBuffer(string.substring(0, index));

                sb.append((char)Integer.parseInt(
                    string.substring(index + 2, index + 6), 16));
                sb.append(string.substring(index + 6));
                string = sb.toString();
            }
        }
        return string;
    }

    public Set<Attribute> makeAttributes(List<Object> names) {
        Set<Attribute> set = new HashSet<>(Math.max(1, names.size()));

        for (int counter = 0; counter < names.size(); counter++) {
            set.add(makeAttribute((String)names.get(counter)));
        }
        return set;
    }
}