Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.core.databinding
Bundle-Version: 1.13.700.qualifier
Bundle-Version: 1.13.800.qualifier
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Export-Package: org.eclipse.core.databinding;version="1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@
* The first type parameter of {@link Converter} is set to {@link Object} to
* preserve backwards compatibility, but the argument is meant to always be a
* {@link Number}.
* <p>
* This class is a variant of the class with the same name in the parent
* package, but it uses {@code java.text} instead of {@code com.ibm.icu}.
* <p>
* Methods on this class that don't take an argument number format use ICU if it
* is available on the classpath, otherwise they use {@code java.text}.
*
* @since 1.9
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@
* The first type parameter of {@link NumberFormatConverter} is set to
* {@link Object} to preserve backwards compatibility, but the argument is meant
* to always be a {@link String}.
* <p>
* This class is a variant of the class with the same name in the parent
* package, but it uses {@code java.text} instead of {@code com.ibm.icu}.
* <p>
* Methods on this class that don't take an argument number format use ICU if it
* is available on the classpath, otherwise they use {@code java.text}.
*
* @param <T> The type to which values are converted.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
******************************************************************************/
package org.eclipse.core.internal.databinding.conversion;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.Format;
Expand All @@ -24,8 +22,7 @@

/**
* Converts a Number to a String using <code>Format.format(...)</code>. This
* class is thread safe. This class is used to share code between converters
* that are based on ICU and java.text.
* class is thread safe.
*
* @since 1.9
*/
Expand All @@ -37,30 +34,6 @@ public class AbstractNumberToStringConverter extends Converter<Object, String> {
private boolean fromTypeIsBigInteger;
private boolean fromTypeIsBigDecimal;

static Class<?> icuBigDecimal = null;
static Constructor<?> icuBigDecimalCtr = null;
static Class<?> icuDecimalFormat = null;

{
/*
* If the full ICU4J library is available, we use the ICU BigDecimal class to
* support proper formatting and parsing of java.math.BigDecimal.
*
* The version of ICU NumberFormat (DecimalFormat) included in eclipse excludes
* support for java.math.BigDecimal, and if used falls back to converting as an
* unknown Number type via doubleValue(), which is undesirable.
*
* See Bug #180392.
*/
try {
icuBigDecimal = Class.forName("com.ibm.icu.math.BigDecimal"); //$NON-NLS-1$
icuBigDecimalCtr = icuBigDecimal.getConstructor(BigInteger.class, int.class);
icuDecimalFormat = Class.forName("com.ibm.icu.text.DecimalFormat"); //$NON-NLS-1$
// System.out.println("DEBUG: Full ICU4J support state: icuBigDecimal="+(icuBigDecimal != null)+", icuBigDecimalCtr="+(icuBigDecimalCtr != null)); //$NON-NLS-1$ //$NON-NLS-2$
} catch (ClassNotFoundException | NoSuchMethodException e) {
}
}

/**
* Constructs a new instance.
* <p>
Expand Down Expand Up @@ -123,17 +96,6 @@ public String convert(Object fromObject) {
result = numberFormat.format(number);
}
} else if (fromTypeIsBigDecimal) {
if (icuBigDecimal != null && icuBigDecimalCtr != null && icuDecimalFormat != null
&& icuDecimalFormat.isInstance(numberFormat)) {
// Full ICU4J present. Convert java.math.BigDecimal to ICU BigDecimal to format.
// Bug #180392.
BigDecimal o = (BigDecimal) fromObject;
try {
fromObject = icuBigDecimalCtr.newInstance(o.unscaledValue(), Integer.valueOf(o.scale()));
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
}
// Otherwise, replacement plugin present and supports java.math.BigDecimal.
}
synchronized (numberFormat) {
result = numberFormat.format(fromObject);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
******************************************************************************/
package org.eclipse.core.internal.databinding.conversion;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.Format;
Expand All @@ -25,8 +23,7 @@

/**
* Converts a String to a Number using <code>Format.parse(...)</code>. This
* class is thread safe. This class is used to share code between converters
* that are based on ICU and java.text.
* class is thread safe.
*
* @param <T> The type to which values are converted.
*/
Expand Down Expand Up @@ -75,32 +72,6 @@ public class AbstractStringToNumberConverter<T extends Number> extends NumberFor
protected static final Byte MIN_BYTE = Byte.valueOf(Byte.MIN_VALUE);
protected static final Byte MAX_BYTE = Byte.valueOf(Byte.MAX_VALUE);

static Class<?> icuBigDecimal = null;
static Method icuBigDecimalScale = null;
static Method icuBigDecimalUnscaledValue = null;

{
/*
* If the full ICU4J library is available, we use the ICU BigDecimal
* class to support proper formatting and parsing of java.math.BigDecimal.
*
* The version of ICU NumberFormat (DecimalFormat) included in eclipse excludes
* support for java.math.BigDecimal, and if used falls back to converting as
* an unknown Number type via doubleValue(), which is undesirable.
*
* See Bug #180392.
*/
try {
icuBigDecimal = Class.forName("com.ibm.icu.math.BigDecimal"); //$NON-NLS-1$
icuBigDecimalScale = icuBigDecimal.getMethod("scale"); //$NON-NLS-1$
icuBigDecimalUnscaledValue = icuBigDecimal.getMethod("unscaledValue"); //$NON-NLS-1$
/* System.out.println("DEBUG: Full ICU4J support state: icuBigDecimal="+ //$NON-NLS-1$
(icuBigDecimal != null)+", icuBigDecimalScale="+(icuBigDecimalScale != null)+ //$NON-NLS-1$
", icuBigDecimalUnscaledValue="+(icuBigDecimalUnscaledValue != null)); //$NON-NLS-1$ */
}
catch(ClassNotFoundException | NoSuchMethodException e) {}
}

/**
* @param numberFormat used to parse the strings numbers.
* @param toType target number type.
Expand Down Expand Up @@ -194,27 +165,16 @@ public T convert(Object fromObject) {
return (T) new BigDecimal((BigInteger) n);
} else if(n instanceof BigDecimal) {
return (T) n;
} else if(icuBigDecimal != null && icuBigDecimal.isInstance(n)) {
try {
// Get ICU BigDecimal value and use to construct java.math.BigDecimal
int scale = ((Integer) icuBigDecimalScale.invoke(n)).intValue();
BigInteger unscaledValue = (BigInteger) icuBigDecimalUnscaledValue.invoke(n);
return (T) new java.math.BigDecimal(unscaledValue, scale);
} catch(IllegalAccessException e) {
throw new IllegalArgumentException("Error (IllegalAccessException) converting BigDecimal using ICU"); //$NON-NLS-1$
} catch(InvocationTargetException e) {
throw new IllegalArgumentException("Error (InvocationTargetException) converting BigDecimal using ICU"); //$NON-NLS-1$
}
} else if(n instanceof Double) {
BigDecimal bd = BigDecimal.valueOf(n.doubleValue());
if (bd.scale() == 0) {
return (T) bd;
}
throw new IllegalArgumentException("Non-integral Double value returned from NumberFormat " + //$NON-NLS-1$
"which cannot be accurately stored in a BigDecimal due to lost precision. " + //$NON-NLS-1$
"Consider using ICU4J or Java 5 which can properly format and parse these types."); //$NON-NLS-1$
"which cannot be accurately stored in a BigDecimal due to lost precision."); //$NON-NLS-1$
}
} else if (Short.class.equals(boxedType)) {
}
else if (Short.class.equals(boxedType)) {
if (StringToNumberParser.inShortRange(result.getNumber())) {
return (T) Short.valueOf(result.getNumber().shortValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@

package org.eclipse.core.internal.databinding.conversion;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.function.Supplier;

import org.eclipse.core.internal.databinding.BindingMessages;

Expand All @@ -37,12 +35,6 @@ public class StringToNumberParser {
private static final BigDecimal DOUBLE_MAX_BIG_DECIMAL = BigDecimal.valueOf(Double.MAX_VALUE);
private static final BigDecimal DOUBLE_MIN_BIG_DECIMAL = BigDecimal.valueOf(-Double.MAX_VALUE);

private static final Supplier<Format> GET_INSTANCE = findMethod(NumberFormat::getInstance, "getInstance"); //$NON-NLS-1$
private static final Supplier<Format> GET_NUMBER_INSTANCE = findMethod(NumberFormat::getNumberInstance,
"getNumberInstance"); //$NON-NLS-1$
private static final Supplier<Format> GET_INTEGER_INSTANCE = findMethod(NumberFormat::getIntegerInstance,
"getIntegerInstance"); //$NON-NLS-1$

/**
* @return result
*/
Expand Down Expand Up @@ -181,12 +173,6 @@ private static boolean checkInteger(Number number, int bitLength) {
} else if (number instanceof BigDecimal) {
bigInteger = ((BigDecimal) number).toBigInteger();
} else {
/*
* The else is necessary as the ICU4J plugin has it's own BigDecimal
* implementation which isn't part of the replacement plugin. So
* that this will work we fall back on the double value of the
* number.
*/
bigInteger = BigDecimal.valueOf(number.doubleValue()).toBigInteger();
}

Expand Down Expand Up @@ -241,16 +227,6 @@ private static boolean checkDecimal(Number number, BigDecimal min,
} else if (number instanceof BigDecimal) {
bigDecimal = (BigDecimal) number;
} else {
/*
* The else is necessary as the ICU4J plugin has it's own BigDecimal
* implementation which isn't part of the replacement plugin. So
* that this will work we fall back on the double value of the
* number.
*/
// if this is ever taken out, take care to un-comment the throw
// clause and the if condition below, they were commented because
// the
// compiler complained about dead code..
double doubleValue = number.doubleValue();

if (!Double.isNaN(doubleValue) && !Double.isInfinite(doubleValue)) {
Expand All @@ -262,9 +238,6 @@ private static boolean checkDecimal(Number number, BigDecimal min,

/* if (bigDecimal != null) */return max.compareTo(bigDecimal) >= 0
&& min.compareTo(bigDecimal) <= 0;

// throw new IllegalArgumentException(
// "Number of type [" + number.getClass().getName() + "] is not supported."); //$NON-NLS-1$ //$NON-NLS-2$
}

/**
Expand Down Expand Up @@ -302,24 +275,20 @@ public static boolean inByteRange(Number number) {

/**
* Returns the default number format.
* {@code com.ibm.icu.text.NumberFormat.getNumberInstance()} if it is available,
* otherwise {@code java.text.NumberFormat.getNumberInstance()}.
*
* @return the number format
*/
public static Format getDefaultFormat() {
return GET_INSTANCE.get();
return NumberFormat.getInstance();
}

/**
* Returns the default number format.
* {@code com.ibm.icu.text.NumberFormat.getNumberInstance()} if it is available,
* otherwise {@code java.text.NumberFormat.getNumberInstance()}.
*
* @return the number format
*/
public static Format getDefaultBigDecimalFormat() {
Format format = GET_NUMBER_INSTANCE.get();
Format format = NumberFormat.getNumberInstance();
if (format instanceof DecimalFormat) {
((DecimalFormat) format).setParseBigDecimal(true);
}
Expand All @@ -328,59 +297,32 @@ public static Format getDefaultBigDecimalFormat() {

/**
* Returns the default number format.
* {@code com.ibm.icu.text.NumberFormat.getNumberInstance()} if ICU is
* available, otherwise {@code java.text.NumberFormat.getNumberInstance()}.
*
* @return the number format
*/
public static Format getDefaultNumberFormat() {
return GET_NUMBER_INSTANCE.get();
return NumberFormat.getNumberInstance();
}

/**
* Returns the default integer format.
* {@code com.ibm.icu.text.NumberFormat.getIntegerInstance()} if ICU is
* available, otherwise {@code java.text.NumberFormat.getIntegerInstance()}.
*
* @return the number format
*/
public static Format getDefaultIntegerFormat() {
return GET_INTEGER_INSTANCE.get();
return NumberFormat.getIntegerInstance();
}

/**
* Returns the default integer format.
* {@code com.ibm.icu.text.NumberFormat.getIntegerInstance()} if ICU is
* available, otherwise {@code java.text.NumberFormat.getIntegerInstance()}.
*
* @return the number format
*/
public static Format getDefaultIntegerBigDecimalFormat() {
Format format = GET_INTEGER_INSTANCE.get();
Format format = NumberFormat.getIntegerInstance();
if (format instanceof DecimalFormat) {
((DecimalFormat) format).setParseBigDecimal(true);
}
return format;
}

/**
* Creates a factory for {@link Format}s. The factory uses ICU if it is
* available on the class path, otherwise it uses the given supplier.
*/
private static Supplier<Format> findMethod(Supplier<Format> javaTextMethod, String methodName) {
try {
Method method = Class.forName("com.ibm.icu.text.NumberFormat").getMethod(methodName); //$NON-NLS-1$
return () -> {
try {
return (Format) method.invoke(null);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e); // Should never happen
}
};
} catch (ClassNotFoundException | SecurityException e) {
return javaTextMethod;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e); // Should never happen
}
}
}
5 changes: 2 additions & 3 deletions docs/JFaceDataBinding.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ This page shows you how to create a simple example application using data bindin
6. In the plug-in manifest editor, switch to the 'Dependencies' tab.
7. Under 'Required Plug-ins', click 'Add...'.
8. In the dialog, type '*databinding'. (Note the leading wildcard character.)
9. Multi-select **org.eclipse.core.databinding**, **org.eclipse.core.databinding.beans**, **org.eclipse.core.databinding.property**, **org.eclipse.jface.databinding**, and **com.ibm.icu**, and click 'OK'.
9. Multi-select **org.eclipse.core.databinding**, **org.eclipse.core.databinding.beans**, **org.eclipse.core.databinding.property**, and **org.eclipse.jface.databinding**, and click 'OK'.
10. Save and then close the plug-in manifest editor.
11. Create a new Java package (File > New > Package) and pick a name for it, e.g. 'starting'.
12. Create a new Java class (File > New > Class) in that package, called 'GettingStarted'.
Expand Down Expand Up @@ -375,7 +375,7 @@ Implementation Design Principles
--------------------------------

1. It's best for the converter to be immutable. This will allow for greater reuse of the instance especially across threads.
2. Synchronize during convert(...) if necessary. A good example of this is using `com.ibm.icu.text.NumberFormat` in a converter. NumberFormat expects to be externally synchronized as the state of NumberFormat changes during formatting and parsing. In order to be used across threads access to the internal NumberFormat must be synchronized.
2. Synchronize during convert(...) if necessary.
3. If the converter is converting to a primitive from an object ensure null is handled.

# Validators
Expand Down Expand Up @@ -497,7 +497,6 @@ For an example snippet, see the [Master Detail snippet](#Snippets).
The core Data Binding bundle has the following dependencies:

* org.eclipse.equinox.common (about 150 KB). We are currently using Assert, IStatus, and ListenerList from equinox.common, but we might potentially use more classes or interfaces from equinox.common in the future, such as e.g. ISafeRunnable/SafeRunner, IProgressMonitor, and IAdaptable.
* Databinding in Eclipse versions earlier than 4.16 had a dependency on ICU4J. 4.16 and later can use ICU if it is available but does not depend on it. (ICU4J is about 4MB for the real thing, or 100KB for the replacement bundle com.ibm.icu.base which is available from the Eclipse Project download pages.)

The data binding framework will run without OSGi. There are optional dependencies on the packages org.osgi.framework, org.osgi.util.tracker, and org.eclipse.osgi.framework.log which allow us to log errors and warnings to the common log if OSGi is available. You can also inject a logger yourself by calling org.eclipse.core.databinding.util.Policy.setLog(), very similar to how this is solved in JFace.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ public class StringToNumberConverterTest {
public void setUp() throws Exception {
numberFormat = StringToNumberParser.getDefaultNumberFormat();

// Use reflection to work for both ICU and java.text
numberFormat.getClass().getMethod("setMaximumFractionDigits", int.class).invoke(numberFormat, 305);
numberFormat.getClass().getMethod("setParseBigDecimal", boolean.class).invoke(numberFormat, true);
if (numberFormat instanceof DecimalFormat) {
((DecimalFormat) numberFormat).setMaximumFractionDigits(305);
((DecimalFormat) numberFormat).setParseBigDecimal(true);
}

numberIntegerFormat = NumberFormat.getIntegerInstance();
}
Expand Down
Loading