From 86bb91a4fef8d0824dbe8596d7034eb15425428b Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Tue, 14 Oct 2014 13:30:38 -0400 Subject: [PATCH] First useable commit --- .../text/CharacterIteratorFieldDelegate.java | 53 +++++- .../moparisthebest/text/DecimalFormat.java | 154 ++++++++++-------- .../com/moparisthebest/text/DigitList.java | 11 +- .../moparisthebest/text/FieldDelegate.java | 77 ++++++++- 4 files changed, 205 insertions(+), 90 deletions(-) diff --git a/src/main/java/com/moparisthebest/text/CharacterIteratorFieldDelegate.java b/src/main/java/com/moparisthebest/text/CharacterIteratorFieldDelegate.java index 9e22ee7..0b78ab8 100644 --- a/src/main/java/com/moparisthebest/text/CharacterIteratorFieldDelegate.java +++ b/src/main/java/com/moparisthebest/text/CharacterIteratorFieldDelegate.java @@ -22,8 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.text; +package com.moparisthebest.text; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.Format; import java.util.ArrayList; /** @@ -31,9 +37,28 @@ import java.util.ArrayList; * into a resulting AttributedCharacterIterator. The resulting * AttributedCharacterIterator can be retrieved by way of * the getIterator method. - * */ -class CharacterIteratorFieldDelegate implements Format.FieldDelegate { +class CharacterIteratorFieldDelegate extends FieldDelegate { + + private static final Constructor iterableConstructor; + private static final Method length; + + static { + Constructor cons = null; + Method l = null; + try { + cons = AttributedString.class.getDeclaredConstructor(AttributedCharacterIterator[].class); + cons.setAccessible(true); + + l = AttributedString.class.getDeclaredMethod("length"); + l.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + iterableConstructor = cons; + length = l; + } + /** * Array of AttributeStrings. Whenever formatted is invoked * for a region > size, a new instance of AttributedString is added to @@ -50,6 +75,7 @@ class CharacterIteratorFieldDelegate implements Format.FieldDelegate { CharacterIteratorFieldDelegate() { + super((Object)null); attributedStrings = new ArrayList<>(); } @@ -63,13 +89,19 @@ class CharacterIteratorFieldDelegate implements Format.FieldDelegate { while (start < index) { AttributedString as = attributedStrings. - get(asIndex--); - int newIndex = index - as.length(); + get(asIndex--); + int asLength; + try { + asLength = (Integer) length.invoke(as); // was: as.length(); + } catch (Throwable e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + int newIndex = index - asLength; int aStart = Math.max(0, start - newIndex); as.addAttribute(attr, value, aStart, Math.min( - end - start, as.length() - aStart) + - aStart); + end - start, asLength - aStart) + + aStart); index = newIndex; } } @@ -119,6 +151,11 @@ class CharacterIteratorFieldDelegate implements Format.FieldDelegate { iterators[counter] = attributedStrings. get(counter).getIterator(); } - return new AttributedString(iterators).getIterator(); + try { + return iterableConstructor.newInstance(iterators).getIterator(); // was: new AttributedString(iterators).getIterator(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + } } diff --git a/src/main/java/com/moparisthebest/text/DecimalFormat.java b/src/main/java/com/moparisthebest/text/DecimalFormat.java index f89fd15..a7780c2 100644 --- a/src/main/java/com/moparisthebest/text/DecimalFormat.java +++ b/src/main/java/com/moparisthebest/text/DecimalFormat.java @@ -36,25 +36,23 @@ * */ -package java.text; +package com.moparisthebest.text; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.text.*; import java.text.spi.NumberFormatProvider; import java.util.ArrayList; import java.util.Currency; import java.util.Locale; -import java.util.ResourceBundle; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import sun.util.locale.provider.LocaleProviderAdapter; -import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * DecimalFormat is a concrete subclass of @@ -378,7 +376,40 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; * @author Mark Davis * @author Alan Liu */ -public class DecimalFormat extends NumberFormat { +public class DecimalFormat extends java.text.DecimalFormat { + + public static final String DECIMAL_FORMAT_EXCEPTION = "com.moparisthebest.text.DecimalFormat will not function"; + private static final FieldPosition dontCareFieldPositionInstance; // = DontCareFieldPosition.INSTANCE + + private static final Class resourceBundleBasedAdapter; + private static final Method getAdapter, getResourceBundleBased, getLocaleResources, getNumberPatterns; + + static { + FieldPosition dcfpi = null; + Class rbba = null; + Method ga = null, grbb = null, glr = null, gnp = null; + try { + final java.lang.reflect.Field instance = Class.forName("java.text.DontCareFieldPosition").getDeclaredField("INSTANCE"); + instance.setAccessible(true); + dcfpi = (FieldPosition) instance.get(null); + + final Class localeProviderAdapter = Class.forName("sun.util.locale.provider.LocaleProviderAdapter"); + rbba = Class.forName("sun.util.locale.provider.ResourceBundleBasedAdapter"); + ga = localeProviderAdapter.getMethod("getAdapter", java.lang.Class.class, Locale.class); + grbb = localeProviderAdapter.getMethod("getResourceBundleBased"); + glr = localeProviderAdapter.getMethod("getLocaleResources", Locale.class); + final Class localeResources = Class.forName("sun.util.locale.provider.LocaleResources"); + gnp = localeResources.getMethod("getNumberPatterns"); + } catch (Throwable e) { + throw new RuntimeException(DECIMAL_FORMAT_EXCEPTION, e); + } + dontCareFieldPositionInstance = dcfpi; + resourceBundleBasedAdapter = rbba; + getAdapter = ga; + getResourceBundleBased = grbb; + getLocaleResources = glr; + getNumberPatterns = gnp; + } /** * Creates a DecimalFormat using the default pattern and symbols @@ -399,12 +430,39 @@ public class DecimalFormat extends NumberFormat { public DecimalFormat() { // Get the pattern for the default locale. Locale def = Locale.getDefault(Locale.Category.FORMAT); + String[] all; + /* + // cannot use this code because javac refuses to let us use these classes... LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, def); if (!(adapter instanceof ResourceBundleBasedAdapter)) { adapter = LocaleProviderAdapter.getResourceBundleBased(); } String[] all = adapter.getLocaleResources(def).getNumberPatterns(); + // original prints: + // all: [#,##0.###;-#,##0.###, ¤#,##0.00;(¤#,##0.00), #,##0%] + // all[0]: #,##0.###;-#,##0.### + */ + try { + // must recreate the above code using reflection, yuck + Object adapter = getAdapter.invoke(null, NumberFormatProvider.class, def); + if (!resourceBundleBasedAdapter.isInstance(adapter)) { + adapter = getResourceBundleBased.invoke(null); + } + all = (String[]) getNumberPatterns.invoke(getLocaleResources.invoke(adapter, def)); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(DECIMAL_FORMAT_EXCEPTION, e); + } + + /* + System.out.println("all: "+java.util.Arrays.toString(all)); + System.out.println("all[0]: "+all[0]); + + // reflection prints: + // all: [#,##0.###;-#,##0.###, ¤#,##0.00;(¤#,##0.00), #,##0%] + // all[0]: #,##0.###;-#,##0.### + */ + // Always applyPattern after the symbols are set this.symbols = DecimalFormatSymbols.getInstance(def); applyPattern(all[0], false); @@ -464,50 +522,6 @@ public class DecimalFormat extends NumberFormat { applyPattern(pattern, false); } - - // Overrides - /** - * Formats a number and appends the resulting text to the given string - * buffer. - * The number can be of any subclass of {@link java.lang.Number}. - *

- * This implementation uses the maximum precision permitted. - * @param number the number to format - * @param toAppendTo the StringBuffer to which the formatted - * text is to be appended - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @return the value passed in as toAppendTo - * @exception IllegalArgumentException if number is - * null or not an instance of Number. - * @exception NullPointerException if toAppendTo or - * pos is null - * @exception ArithmeticException if rounding is needed with rounding - * mode being set to RoundingMode.UNNECESSARY - * @see java.text.FieldPosition - */ - @Override - public final StringBuffer format(Object number, - StringBuffer toAppendTo, - FieldPosition pos) { - if (number instanceof Long || number instanceof Integer || - number instanceof Short || number instanceof Byte || - number instanceof AtomicInteger || - number instanceof AtomicLong || - (number instanceof BigInteger && - ((BigInteger)number).bitLength () < 64)) { - return format(((Number)number).longValue(), toAppendTo, pos); - } else if (number instanceof BigDecimal) { - return format((BigDecimal)number, toAppendTo, pos); - } else if (number instanceof BigInteger) { - return format((BigInteger)number, toAppendTo, pos); - } else if (number instanceof Number) { - return format(((Number)number).doubleValue(), toAppendTo, pos); - } else { - throw new IllegalArgumentException("Cannot format given Object as a Number"); - } - } - /** * Formats a double to produce a string. * @param number The double to format @@ -525,7 +539,7 @@ public class DecimalFormat extends NumberFormat { // If fieldPosition is a DontCareFieldPosition instance we can // try to go to fast-path code. boolean tryFastPath = false; - if (fieldPosition == DontCareFieldPosition.INSTANCE) + if (fieldPosition == dontCareFieldPositionInstance) tryFastPath = true; else { fieldPosition.setBeginIndex(0); @@ -541,7 +555,7 @@ public class DecimalFormat extends NumberFormat { } // if fast-path could not work, we fallback to standard code. - return format(number, result, fieldPosition.getFieldDelegate()); + return format(number, result, new FieldDelegate(fieldPosition)); } /** @@ -643,7 +657,7 @@ public class DecimalFormat extends NumberFormat { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); - return format(number, result, fieldPosition.getFieldDelegate()); + return format(number, result, new FieldDelegate(fieldPosition)); } /** @@ -729,7 +743,7 @@ public class DecimalFormat extends NumberFormat { FieldPosition fieldPosition) { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); - return format(number, result, fieldPosition.getFieldDelegate()); + return format(number, result, new FieldDelegate(fieldPosition)); } /** @@ -783,7 +797,7 @@ public class DecimalFormat extends NumberFormat { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); - return format(number, result, fieldPosition.getFieldDelegate(), false); + return format(number, result, new FieldDelegate(fieldPosition), false); } /** @@ -1994,8 +2008,8 @@ public class DecimalFormat extends NumberFormat { @Override public Number parse(String text, ParsePosition pos) { // special case NaN - if (text.regionMatches(pos.index, symbols.getNaN(), 0, symbols.getNaN().length())) { - pos.index = pos.index + symbols.getNaN().length(); + if (text.regionMatches(pos.getIndex(), symbols.getNaN(), 0, symbols.getNaN().length())) { + pos.setIndex(pos.getIndex() + symbols.getNaN().length()); return new Double(Double.NaN); } @@ -2140,8 +2154,8 @@ public class DecimalFormat extends NumberFormat { String positivePrefix, String negativePrefix, DigitList digits, boolean isExponent, boolean status[]) { - int position = parsePosition.index; - int oldStart = parsePosition.index; + int position = parsePosition.getIndex(); + int oldStart = parsePosition.getIndex(); int backup; boolean gotPositive, gotNegative; @@ -2164,7 +2178,7 @@ public class DecimalFormat extends NumberFormat { } else if (gotNegative) { position += negativePrefix.length(); } else { - parsePosition.errorIndex = position; + parsePosition.setErrorIndex(position); return false; } @@ -2271,7 +2285,7 @@ public class DecimalFormat extends NumberFormat { if (subparse(text, pos, "", Character.toString(symbols.getMinusSign()), exponentDigits, true, stat) && exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) { - position = pos.index; // Advance past the exponent + position = pos.getIndex(); // Advance past the exponent exponent = (int)exponentDigits.getLong(); if (!stat[STATUS_POSITIVE]) { exponent = -exponent; @@ -2301,8 +2315,8 @@ public class DecimalFormat extends NumberFormat { // parse "$" with pattern "$#0.00". (return index 0 and error // index 1). if (!sawDigit && digitCount == 0) { - parsePosition.index = oldStart; - parsePosition.errorIndex = oldStart; + parsePosition.setIndex(oldStart); + parsePosition.setIndex(oldStart); return false; } } @@ -2329,19 +2343,19 @@ public class DecimalFormat extends NumberFormat { // fail if neither or both if (gotPositive == gotNegative) { - parsePosition.errorIndex = position; + parsePosition.setErrorIndex(position); return false; } - parsePosition.index = position + - (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success! + parsePosition.setIndex(position + + (gotPositive ? positiveSuffix.length() : negativeSuffix.length())); // mark success! } else { - parsePosition.index = position; + parsePosition.setIndex(position); } status[STATUS_POSITIVE] = gotPositive; - if (parsePosition.index == oldStart) { - parsePosition.errorIndex = position; + if (parsePosition.getIndex() == oldStart) { + parsePosition.setErrorIndex(position); return false; } return true; diff --git a/src/main/java/com/moparisthebest/text/DigitList.java b/src/main/java/com/moparisthebest/text/DigitList.java index a355944..4e3e4d9 100644 --- a/src/main/java/com/moparisthebest/text/DigitList.java +++ b/src/main/java/com/moparisthebest/text/DigitList.java @@ -36,7 +36,7 @@ * */ -package java.text; +package com.moparisthebest.text; import java.math.BigDecimal; import java.math.BigInteger; @@ -62,12 +62,11 @@ import java.math.RoundingMode; * derived by placing all the digits of the list to the right of the * decimal point, by 10^exponent. * - * @see Locale - * @see Format - * @see NumberFormat + * @see java.text.Format + * @see java.text.NumberFormat * @see DecimalFormat - * @see ChoiceFormat - * @see MessageFormat + * @see java.text.ChoiceFormat + * @see java.text.MessageFormat * @author Mark Davis, Alan Liu */ final class DigitList implements Cloneable { diff --git a/src/main/java/com/moparisthebest/text/FieldDelegate.java b/src/main/java/com/moparisthebest/text/FieldDelegate.java index 9f639ec..0487d48 100644 --- a/src/main/java/com/moparisthebest/text/FieldDelegate.java +++ b/src/main/java/com/moparisthebest/text/FieldDelegate.java @@ -36,9 +36,17 @@ * */ -package java.text; +package com.moparisthebest.text; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.FieldPosition; +import java.text.Format; /** + * This implementation delegates to the real interface/implementation + * found in java.text.Format$FieldDelegate + *

* FieldDelegate is notified by the various Format * implementations as they are formatting the Objects. This allows for * storage of the individual sections of the formatted String for @@ -48,10 +56,54 @@ package java.text; * Delegates should NOT assume that the Format will notify * the delegate of fields in any particular order. * - * @see FieldPosition#getFieldDelegate - * @see CharacterIteratorFieldDelegate + * @see java.text.FieldPosition#getFieldDelegate + * @see java.text.CharacterIteratorFieldDelegate */ -interface FieldDelegate { +public class FieldDelegate { + + private static final Method getFieldDelegate; + private static final Method format1; + private static final Method format2; + + static { + Method gfd = null, f1 = null, f2 = null; + try { + gfd = FieldPosition.class.getDeclaredMethod("getFieldDelegate"); + gfd.setAccessible(true); + + final Class realFieldDelegate = Class.forName("java.text.Format$FieldDelegate"); + + f1 = realFieldDelegate.getDeclaredMethod("formatted", Format.Field.class, Object.class, int.class, int.class, StringBuffer.class); + f1.setAccessible(true); + + f2 = realFieldDelegate.getDeclaredMethod("formatted", int.class, Format.Field.class, Object.class, int.class, int.class, StringBuffer.class); + f2.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + getFieldDelegate = gfd; + format1 = f1; + format2 = f2; + } + + private final Object fieldDelegate; + + public FieldDelegate(final FieldPosition fieldPosition) { + //this(fieldPosition.getFieldDelegate()); + Object fieldDelegate = null; + if (fieldPosition != null) + try { + fieldDelegate = getFieldDelegate.invoke(fieldPosition); + } catch (Throwable e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + this.fieldDelegate = fieldDelegate; + } + + public FieldDelegate(final Object fieldDelegate) { + this.fieldDelegate = fieldDelegate; + } + /** * Notified when a particular region of the String is formatted. This * method will be invoked if there is no corresponding integer field id @@ -65,7 +117,14 @@ interface FieldDelegate { * NOT modify it. */ public void formatted(Format.Field attr, Object value, int start, - int end, StringBuffer buffer); + int end, StringBuffer buffer) { + + try { + format1.invoke(this.fieldDelegate, attr, value, start, end, buffer); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + } /** * Notified when a particular region of the String is formatted. @@ -79,5 +138,11 @@ interface FieldDelegate { * NOT modify it. */ public void formatted(int fieldID, Format.Field attr, Object value, - int start, int end, StringBuffer buffer); + int start, int end, StringBuffer buffer) { + try { + format2.invoke(this.fieldDelegate, fieldID, attr, value, start, end, buffer); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(DecimalFormat.DECIMAL_FORMAT_EXCEPTION, e); + } + } }