/*
* Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.application;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A base class for converting arbitrary types to and from Strings, as well as
* a registry of ResourceConverter implementations.
* <p>
* The <tt>supportsType</tt> method defines what types a ResourceConverter supports.
* By default it returns true for classes that are equal to the constructor's
* <tt>type</tt> argument. The <tt>parseType</tt> methods converts a string
* the ResourceConverter's supported type, and the <tt>toString</tt> does the
* inverse, it converts a supported type to a String. Concrete ResourceConverter
* subclasses must override <tt>parseType()</tt> and, in most cases, the
* <tt>toString</tt> method as well.
* <p>
* This class maintains a registry of ResourceConverters.
* The <tt>forType</tt> method returns the first ResourceConverter that
* supports a particular type, new ResourceConverters can be added with
* <tt>register()</tt>. A small set of generic ResourceConverters are
* registered by default. They support the following types:
* <ul>
* <li><tt>Boolean</tt></li>
* <li><tt>Integer</tt></li>
* <li><tt>Float</tt></li>
* <li><tt>Double</tt></li>
* <li><tt>Long</tt></li>
* <li><tt>Short</tt></li>
* <li><tt>Byte</tt></li>
* <li><tt>MessageFormat</tt></li>
* <li><tt>URL</tt></li>
* <li><tt>URI</tt></li>
* </ul>
* <p>
* The Boolean ResourceConverter returns true for "true", "on", "yes",
* false otherwise. The other primitive type ResourceConverters rely on
* the corresponding static parse<i>Type</i> method,
* e.g. <tt>Integer.parseInt()</tt>. The MessageFormat
* ResourceConverter just creates MessageFormat object with the string
* as its constructor argument. The URL/URI converters just apply
* the corresponding constructor to the resource string.
*
* @author Hans Muller (Hans.Muller@Sun.COM)
* @see ResourceMap
*/
public abstract class ResourceConverter {
protected final Class type;
public abstract Object parseString(String s, ResourceMap r)
throws ResourceConverterException;
public String toString(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
protected ResourceConverter(Class type) {
if (type == null) {
throw new IllegalArgumentException("null type");
}
this.type = type;
}
private ResourceConverter() { type = null; } // not used
public boolean supportsType(Class testType) {
return type.equals(testType);
}
public static class ResourceConverterException extends Exception {
private final String badString;
private String maybeShorten(String s) {
int n = s.length();
return (n < 128) ? s : s.substring(0, 128) + "...[" + (n-128) + " more characters]";
}
public ResourceConverterException(String message, String badString, Throwable cause) {
super(message, cause);
this.badString = maybeShorten(badString);
}
public ResourceConverterException(String message, String badString) {
super(message);
this.badString = maybeShorten(badString);
}
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
sb.append(" string: \"");
sb.append(badString);
sb.append("\"");
return sb.toString();
}
}
public static void register(ResourceConverter resourceConverter) {
if (resourceConverter == null) {
throw new IllegalArgumentException("null resourceConverter");
}
resourceConverters.add(resourceConverter);
}
public static ResourceConverter forType(Class type) {
if (type == null) {
throw new IllegalArgumentException("null type");
}
for (ResourceConverter sc: resourceConverters) {
if (sc.supportsType(type)) {
return sc;
}
}
return null;
}
private static ResourceConverter[] resourceConvertersArray = {
new BooleanResourceConverter("true", "on", "yes"),
new IntegerResourceConverter(),
new MessageFormatResourceConverter(),
new FloatResourceConverter(),
new DoubleResourceConverter(),
new LongResourceConverter(),
new ShortResourceConverter(),
new ByteResourceConverter(),
new URLResourceConverter(),
new URIResourceConverter()
};
private static List<ResourceConverter> resourceConverters =
new ArrayList<ResourceConverter>(Arrays.asList(resourceConvertersArray));
private static class BooleanResourceConverter extends ResourceConverter {
private final String[] trueStrings;
BooleanResourceConverter(String ... trueStrings) {
super(Boolean.class);
this.trueStrings = trueStrings;
}
@Override
public Object parseString(String s, ResourceMap ignore) {
s = s.trim();
for(String trueString : trueStrings) {
if (s.equalsIgnoreCase(trueString)) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
@Override
public boolean supportsType(Class testType) {
return testType.equals(Boolean.class) || testType.equals(boolean.class);
}
}
private static abstract class NumberResourceConverter extends ResourceConverter {
private final Class primitiveType;
NumberResourceConverter(Class type, Class primitiveType) {
super(type);
this.primitiveType = primitiveType;
}
protected abstract Number parseString(String s) throws NumberFormatException;
@Override
public Object parseString(String s, ResourceMap ignore) throws ResourceConverterException {
try {
return parseString(s);
}
catch (NumberFormatException e) {
throw new ResourceConverterException("invalid " + type.getSimpleName(), s, e);
}
}
@Override
public boolean supportsType(Class testType) {
return testType.equals(type) || testType.equals(primitiveType);
}
}
private static class FloatResourceConverter extends NumberResourceConverter {
FloatResourceConverter() {
super(Float.class, float.class);
}
@Override
protected Number parseString(String s) throws NumberFormatException {
return Float.parseFloat(s);
}
}
private static class DoubleResourceConverter extends NumberResourceConverter {
DoubleResourceConverter() {
super(Double.class, double.class);
}
@Override
protected Number parseString(String s) throws NumberFormatException {
return Double.parseDouble(s);
}
}
private static abstract class INumberResourceConverter extends ResourceConverter {
private final Class primitiveType;
INumberResourceConverter(Class type, Class primitiveType) {
super(type);
this.primitiveType = primitiveType;
}
protected abstract Number parseString(String s, int radix) throws NumberFormatException;
@Override
public Object parseString(String s, ResourceMap ignore) throws ResourceConverterException {
try {
String[] nar = s.split("&"); // number ampersand radix
int radix = (nar.length == 2) ? Integer.parseInt(nar[1]) : -1;
return parseString(nar[0], radix);
}
catch (NumberFormatException e) {
throw new ResourceConverterException("invalid " + type.getSimpleName(), s, e);
}
}
@Override
public boolean supportsType(Class testType) {
return testType.equals(type) || testType.equals(primitiveType);
}
}
private static class ByteResourceConverter extends INumberResourceConverter {
ByteResourceConverter() {
super(Byte.class, byte.class);
}
@Override
protected Number parseString(String s, int radix) throws NumberFormatException {
return (radix == -1) ? Byte.decode(s) : Byte.parseByte(s, radix);
}
}
private static class IntegerResourceConverter extends INumberResourceConverter {
IntegerResourceConverter() {
super(Integer.class, int.class);
}
@Override
protected Number parseString(String s, int radix) throws NumberFormatException {
return (radix == -1) ? Integer.decode(s) : Integer.parseInt(s, radix);
}
}
private static class LongResourceConverter extends INumberResourceConverter {
LongResourceConverter() {
super(Long.class, long.class);
}
@Override
protected Number parseString(String s, int radix) throws NumberFormatException {
return (radix == -1) ? Long.decode(s) : Long.parseLong(s, radix);
}
}
private static class ShortResourceConverter extends INumberResourceConverter {
ShortResourceConverter() {
super(Short.class, short.class);
}
@Override
protected Number parseString(String s, int radix) throws NumberFormatException {
return (radix == -1) ? Short.decode(s) : Short.parseShort(s, radix);
}
}
private static class MessageFormatResourceConverter extends ResourceConverter {
MessageFormatResourceConverter() {
super(MessageFormat.class);
}
@Override
public Object parseString(String s, ResourceMap ignore) {
return new MessageFormat(s);
}
}
private static class URLResourceConverter extends ResourceConverter {
URLResourceConverter() {
super(URL.class);
}
@Override
public Object parseString(String s, ResourceMap ignore) throws ResourceConverterException {
try {
return new URL(s);
}
catch(MalformedURLException e) {
throw new ResourceConverterException("invalid URL", s, e);
}
}
}
private static class URIResourceConverter extends ResourceConverter {
URIResourceConverter() {
super(URI.class);
}
@Override
public Object parseString(String s, ResourceMap ignore) throws ResourceConverterException {
try {
return new URI(s);
}
catch(URISyntaxException e) {
throw new ResourceConverterException("invalid URI", s, e);
}
}
}
}