/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.xmlcode; import java.awt.Color; import java.awt.Font; import java.awt.Point; import java.awt.Rectangle; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.StringTokenizer; /** * Utility class used to encode objects * * @author sguerin */ public class StringEncoder { private static StringEncoder defaultInstance = new StringEncoder(); public static String encodeBoolean(boolean aBoolean) { return aBoolean ? "true" : "false"; } public static String encodeByte(byte aByte) { return "" + aByte; } public static String encodeCharacter(char aChar) { return "" + aChar; } public static String encodeDouble(double aDouble) { return "" + aDouble; } public static String encodeFloat(float aFloat) { return "" + aFloat; } public static String encodeInteger(int anInt) { return "" + anInt; } public static String encodeLong(long aLong) { return "" + aLong; } public static String encodeShort(short aShort) { return "" + aShort; } public static boolean decodeAsBoolean(String value) { return value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes"); } public static byte decodeAsByte(String value) { return Byte.parseByte(value); } public static char decodeAsCharacter(String value) { return value.charAt(0); } public static double decodeAsDouble(String value) { try { return Double.parseDouble(value); } catch (NumberFormatException e) { return 0; } } public static float decodeAsFloat(String value) { return Float.parseFloat(value); } public static int decodeAsInteger(String value) { if (value == null) { return -1; } try { return Integer.parseInt(value); } catch (NumberFormatException e) { return (int) decodeAsDouble(value); } } public static long decodeAsLong(String value) { return Long.parseLong(value); } public static short decodeAsShort(String value) { return Short.parseShort(value); } /** * * @param value * @param objectType * @return * @deprecated use the non static method {@link #_decodeObject(String, Class)} */ @Deprecated public static <T> T decodeObject(String value, Class<T> objectType) { return defaultInstance._decodeObject(value, objectType); } /** * * @param object * @return * @deprecated use the non static method {@link #_encodeObject(Object)} */ @Deprecated public static <T> String encodeObject(T object) { return defaultInstance._encodeObject(object); } /** * * @param objectType * @return * @deprecated use the non static method {@link #_converterForClass(Class)} */ @Deprecated public static <T> Converter<T> converterForClass(Class<T> objectType) { return defaultInstance._converterForClass(objectType); } /** * * @param objectType * @return * @deprecated use the non static method {@link #_isConvertable(Class)} */ @Deprecated public static <T> boolean isConvertable(Class<T> objectType) { return defaultInstance._isConvertable(objectType); } /** * Sets date format, under the form <code>"yyyy.MM.dd G 'at' HH:mm:ss a zzz"</code> * * @deprecated use the non static method {@link #_setDateFormat(String)} */ @Deprecated public static void setDateFormat(String aFormat) { defaultInstance._setDateFormat(aFormat); } /** * * @return * @deprecated use the non static method {@link #_getDateFormat()} */ @Deprecated public static String getDateFormat() { return defaultInstance._getDateFormat(); } /** * Return a string representation of a date, according to valid date format * * @deprecated use the non-static method {@link #_getDateRepresentation(Date)} */ @Deprecated public static String getDateRepresentation(Date aDate) { return defaultInstance._getDateRepresentation(aDate); } /** * * @param converter * @return * @deprecated use the non-static method {@link #_addConverter(org.openflexo.xmlcode.StringEncoder.Converter)} */ @Deprecated public static <T> Converter<T> addConverter(Converter<T> converter) { return defaultInstance._addConverter(converter); } /** * @param converter */ public static <T> void removeConverter(Converter<T> converter) { defaultInstance._removeConverter(converter); } public static void initialize() { defaultInstance._initialize(); } public static void reset() { defaultInstance = new StringEncoder(); defaultInstance._initialize(); } /** * Abstract class defining a converter to and from a String for a given class * * @author sguerin */ public static abstract class Converter<T> { protected Class<T> converterClass; public Converter(Class<T> aClass) { super(); converterClass = aClass; } public Class<T> getConverterClass() { return converterClass; } public abstract T convertFromString(String value); public abstract String convertToString(T value); } public static class EnumerationConverter<T> extends Converter<T> { private String _stringRepresationMethodName; public EnumerationConverter(Class<T> enumeration, String stringRepresentationMethodName) { super(enumeration); _stringRepresationMethodName = stringRepresentationMethodName; } @Override public T convertFromString(String value) { if (value == null) { return null; } for (int i = 0; i < converterClass.getEnumConstants().length; i++) { if (value.equals(convertToString(converterClass.getEnumConstants()[i]))) { return converterClass.getEnumConstants()[i]; } } return null; } @Override public String convertToString(T value) { if (value == null) { return null; } try { Method m = value.getClass().getDeclaredMethod(_stringRepresationMethodName, (Class[]) null); return (String) m.invoke(value, (Object[]) null); } catch (NoSuchMethodException e) { System.err.println(_stringRepresationMethodName + " doesn't exist on enum :" + value.getClass().getName()); } catch (InvocationTargetException e) { System.err.println("Invocation of " + _stringRepresationMethodName + " on enum :" + value.getClass().getName() + " caused the following error : " + e.getMessage()); e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } } /** * Class defining how to convert Boolean from/to String * * @author sguerin */ public static class BooleanConverter extends Converter<Boolean> { protected BooleanConverter() { super(Boolean.class); } @Override public Boolean convertFromString(String value) { return Boolean.valueOf(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes")); } @Override public String convertToString(Boolean value) { return value.toString(); } } /** * Class defining how to convert Integer from/to String * * @author sguerin */ public static class NumberConverter extends Converter<Number> { protected NumberConverter() { super(Number.class); } @Override public Number convertFromString(String value) { try { Number returned = Integer.parseInt(value); // System.out.println("Build a integer: "+value); return returned; } catch (NumberFormatException e1) { try { Number returned = Long.parseLong(value); // System.out.println("Build a long: "+value); return returned; } catch (NumberFormatException e2) { try { Number returned = Float.parseFloat(value); // System.out.println("Build a float: "+value); return returned; } catch (NumberFormatException e3) { try { Number returned = Double.parseDouble(value); // System.out.println("Build a double: "+value); return returned; } catch (NumberFormatException e4) { } } } } return null; } @Override public String convertToString(Number value) { return value.toString(); } } /** * Class defining how to convert Integer from/to String * * @author sguerin */ public static class IntegerConverter extends Converter<Integer> { protected IntegerConverter() { super(Integer.class); } @Override public Integer convertFromString(String value) { return Integer.valueOf(value); } @Override public String convertToString(Integer value) { return value.toString(); } } /** * Class defining how to convert Short from/to String * * @author sguerin */ public static class ShortConverter extends Converter<Short> { protected ShortConverter() { super(Short.class); } @Override public Short convertFromString(String value) { return Short.valueOf(value); } @Override public String convertToString(Short value) { return value.toString(); } } /** * Class defining how to convert Long from/to String * * @author sguerin */ public static class LongConverter extends Converter<Long> { protected LongConverter() { super(Long.class); } @Override public Long convertFromString(String value) { return Long.valueOf(value); } @Override public String convertToString(Long value) { return value.toString(); } } /** * Class defining how to convert Float from/to String * * @author sguerin */ public static class FloatConverter extends Converter<Float> { protected FloatConverter() { super(Float.class); } @Override public Float convertFromString(String value) { return Float.valueOf(value); } @Override public String convertToString(Float value) { return value.toString(); } } /** * Class defining how to convert Double from/to String * * @author sguerin */ public static class DoubleConverter extends Converter<Double> { protected DoubleConverter() { super(Double.class); } @Override public Double convertFromString(String value) { return Double.valueOf(value); } @Override public String convertToString(Double value) { return value.toString(); } } /** * Class defining how to convert String from/to String (easy !) * * @author sguerin */ public static class StringConverter extends Converter<String> { protected StringConverter() { super(String.class); } @Override public String convertFromString(String value) { return value; } @Override public String convertToString(String value) { return value; } } /** * Class defining how to convert String from/to Date * * @author sguerin */ public static class DateConverter extends Converter<Date> { /** Specify date format */ protected String _dateFormat = new SimpleDateFormat().toPattern(); public DateConverter() { super(Date.class); } @Override public Date convertFromString(String value) { try { return tryToConvertFromString(value); } catch (ParseException e) { SimpleDateFormat formatter = new SimpleDateFormat(_dateFormat); Date currentTime = new Date(); String dateString = formatter.format(currentTime); System.err.println("Supplied value is not parsable as a date. " + " Date format should be for example " + dateString); return null; } } public Date tryToConvertFromString(String value) throws ParseException { Date returned = null; StringTokenizer st = new StringTokenizer(value, ","); String dateFormat = _dateFormat; String dateAsString = null; if (st.hasMoreTokens()) { dateFormat = st.nextToken(); } if (st.hasMoreTokens()) { dateAsString = st.nextToken(); } if (dateAsString != null) { try { returned = new SimpleDateFormat(dateFormat).parse(dateAsString); } catch (IllegalArgumentException e) { throw new ParseException("While parsing supposed date format: " + e.getMessage(), 0); } } if (returned == null) { throw new ParseException("Cannot parse as a date " + value, 0); } return returned; } @Override public String convertToString(Date date) { if (date != null) { return _dateFormat + "," + new SimpleDateFormat(_dateFormat).format(date); } else { return null; } } /** * Return a string representation of a date, according to valid date format */ public String getDateRepresentation(Date date) { if (date != null) { return new SimpleDateFormat(_dateFormat).format(date); } else { return null; } } } /** * Class defining how to convert String from/to URL * * @author sguerin */ public static class URLConverter extends Converter<URL> { protected URLConverter() { super(URL.class); } @Override public URL convertFromString(String value) { try { return new URL(value); } catch (MalformedURLException e) { System.err.println("Supplied value is not parsable as an URL:" + value); return null; } } @Override public String convertToString(URL anURL) { if (anURL != null) { return anURL.toExternalForm(); } else { return null; } } } /** * Class defining how to convert String from/to File * * @author sguerin */ public static class FileConverter extends Converter<File> { protected FileConverter() { super(File.class); } @Override public File convertFromString(String value) { return new File(value); } @Override public String convertToString(File aFile) { if (aFile != null) { return aFile.getAbsolutePath(); } else { return null; } } } /** * Class defining how to convert String from/to Class * * @author sguerin */ public static class ClassConverter extends Converter<Class> { protected ClassConverter() { super(Class.class); } @Override public Class<?> convertFromString(String value) { if (value == null || value.isEmpty()) { return null; } try { if (value.equals("boolean")) { return Boolean.TYPE; } if (value.equals("int")) { return Integer.TYPE; } if (value.equals("short")) { return Short.TYPE; } if (value.equals("long")) { return Long.TYPE; } if (value.equals("float")) { return Float.TYPE; } if (value.equals("double")) { return Double.TYPE; } if (value.equals("byte")) { return Byte.TYPE; } if (value.equals("char")) { return Character.TYPE; } return Class.forName(value); } catch (ClassNotFoundException e) { // Warns about the exception throw new InvalidDataException("Supplied value represents a class not found: " + value); } catch (NoClassDefFoundError e) { e.printStackTrace(); throw new InvalidDataException("Supplied value triggered a class not found: " + value); } } @Override public String convertToString(Class aClass) { if (aClass != null) { return aClass.getName(); } else { return null; } } } /** * Class defining how to convert String from/to Point * * @author sguerin */ public static class PointConverter extends Converter<Point> { protected PointConverter() { super(Point.class); } @Override public Point convertFromString(String value) { try { Point returned = new Point(); StringTokenizer st = new StringTokenizer(value, ","); if (st.hasMoreTokens()) { returned.x = Integer.parseInt(st.nextToken()); } if (st.hasMoreTokens()) { returned.y = Integer.parseInt(st.nextToken()); } return returned; } catch (NumberFormatException e) { // Warns about the exception System.err.println("Supplied value is not parsable as a Point:" + value); return null; } } @Override public String convertToString(Point aPoint) { if (aPoint != null) { return aPoint.x + "," + aPoint.y; } else { return null; } } } /** * Class defining how to convert String from/to Point * * @author sguerin */ public static class RectangleConverter extends Converter<Rectangle> { protected RectangleConverter() { super(Rectangle.class); } @Override public Rectangle convertFromString(String value) { try { Rectangle returned = new Rectangle(); StringTokenizer st = new StringTokenizer(value, ","); if (st.hasMoreTokens()) { returned.x = Integer.parseInt(st.nextToken()); } if (st.hasMoreTokens()) { returned.y = Integer.parseInt(st.nextToken()); } if (st.hasMoreTokens()) { returned.width = Integer.parseInt(st.nextToken()); } if (st.hasMoreTokens()) { returned.height = Integer.parseInt(st.nextToken()); } return returned; } catch (NumberFormatException e) { // Warns about the exception System.err.println("Supplied value is not parsable as a Rectangle:" + value); return null; } } @Override public String convertToString(Rectangle rect) { if (rect != null) { return rect.x + "," + rect.y + "," + rect.width + "," + rect.height; } else { return null; } } } /** * Class defining how to convert String from/to Color * * @author sguerin */ public static class ColorConverter extends Converter<Color> { protected ColorConverter() { super(Color.class); } @Override public Color convertFromString(String value) { return new Color(redFromString(value), greenFromString(value), blueFromString(value)); } @Override public String convertToString(Color aColor) { return aColor.getRed() + "," + aColor.getGreen() + "," + aColor.getBlue(); } private static int redFromString(String s) { return Integer.parseInt(s.substring(0, s.indexOf(","))); } private static int greenFromString(String s) { return Integer.parseInt(s.substring(s.indexOf(",") + 1, s.lastIndexOf(","))); } private static int blueFromString(String s) { return Integer.parseInt(s.substring(s.lastIndexOf(",") + 1)); } } /** * Class defining how to convert String from/to Font * * @author sguerin */ public static class FontConverter extends Converter<Font> { protected FontConverter() { super(Font.class); } @Override public Font convertFromString(String value) { return new Font(nameFromString(value), styleFromString(value), sizeFromString(value)); } @Override public String convertToString(Font aFont) { return aFont.getName() + "," + aFont.getStyle() + "," + aFont.getSize(); } private static String nameFromString(String s) { return s.substring(0, s.indexOf(",")); } private static int styleFromString(String s) { return Integer.parseInt(s.substring(s.indexOf(",") + 1, s.lastIndexOf(","))); } private static int sizeFromString(String s) { return Integer.parseInt(s.substring(s.lastIndexOf(",") + 1)); } } /** * Hereunder are all the non-static elements of this class. Only those should be used. */ private Hashtable<Class<?>, Converter<?>> converters = new Hashtable<Class<?>, Converter<?>>(); private boolean isInitialized = false; private StringEncoder delegate; public StringEncoder() { } public StringEncoder(StringEncoder delegate, Converter<?>... converters) { super(); this.delegate = delegate; for (Converter<?> converter : converters) { _addConverter(converter); } isInitialized = true; } public List<Converter<?>> getConverters() { List<Converter<?>> converters = new ArrayList<StringEncoder.Converter<?>>(); if (delegate != null) { converters.addAll(delegate.getConverters()); } converters.addAll(this.converters.values()); return converters; } @SuppressWarnings("unchecked") public <T> T _decodeObject(String value, Class<T> objectType) { if (!isInitialized) { _initialize(); } if (value == null) { return null; } Converter<T> converter = _converterForClass(objectType); if (converter != null) { return converter.convertFromString(value); } else if (objectType.isEnum()) { try { return (T) Enum.valueOf((Class) objectType, value); } catch (IllegalArgumentException e) { System.err.println("Could not decode " + value + " as a " + objectType); return null; } } else { throw new InvalidDataException("Supplied value has no converter for type " + objectType.getName()); } } public <T> String _encodeObject(T object) { if (!isInitialized) { _initialize(); } if (object == null) { return null; } Converter<T> converter = (Converter<T>) _converterForClass(object.getClass()); if (converter != null) { return converter.convertToString(object); } else { if (object instanceof StringConvertable) { converter = ((StringConvertable) object).getConverter(); if (converter != null) { // System.out.println ("Registering my own converter"); _addConverter(converter); return converter.convertToString(object); } } else if (object instanceof Enum) { return ((Enum<?>) object).name(); } throw new InvalidDataException("Supplied value has no converter for type " + object.getClass().getName()); } } public <T> Converter<T> _converterForClass(Class<T> objectType) { if (!isInitialized) { _initialize(); } /* * System.out.println ("I've got those converters:"); for (Enumeration e = * converters.keys(); e.hasMoreElements();) { Class key = * (Class)e.nextElement(); Converter converter = * (Converter)converters.get(key); System.out.println ("Key: "+key+" * Converter: "+converter.getConverterClass().getName()); } */ Converter<?> returned; Class<? super T> tryThis = objectType; do { returned = converters.get(tryThis); if (tryThis.equals(Object.class)) { break; } tryThis = tryThis.getSuperclass(); } while (returned == null && tryThis != null); if (returned == null && delegate != null) { return delegate._converterForClass(objectType); } return (Converter<T>) returned; } public <T> boolean _isConvertable(Class<T> objectType) { if (!isInitialized) { _initialize(); } return _converterForClass(objectType) != null; } public <T> boolean _isEncodable(Class<T> objectType) { return _isConvertable(objectType) || objectType.isEnum(); } /** * Sets date format, under the form <code>"yyyy.MM.dd G 'at' HH:mm:ss a zzz"</code> */ public void _setDateFormat(String aFormat) { DateConverter dc = (DateConverter) _converterForClass(Date.class); dc._dateFormat = aFormat; } public String _getDateFormat() { DateConverter dc = (DateConverter) _converterForClass(Date.class); return dc._dateFormat; } /** * Return a string representation of a date, according to valid date format */ public String _getDateRepresentation(Date aDate) { DateConverter dc = (DateConverter) _converterForClass(Date.class); return dc.getDateRepresentation(aDate); } public <T> Converter<T> _addConverter(Converter<T> converter) { converters.put(converter.getConverterClass(), converter); return converter; } /** * @param converter */ public void _removeConverter(Converter<?> converter) { converters.remove(converter.getConverterClass()); } public void _initialize() { if (!isInitialized) { _addConverter(new BooleanConverter()); _addConverter(new IntegerConverter()); _addConverter(new ShortConverter()); _addConverter(new LongConverter()); _addConverter(new FloatConverter()); _addConverter(new DoubleConverter()); _addConverter(new StringConverter()); _addConverter(new DateConverter()); _addConverter(new URLConverter()); _addConverter(new FileConverter()); _addConverter(new ClassConverter()); _addConverter(new PointConverter()); _addConverter(new RectangleConverter()); _addConverter(new ColorConverter()); _addConverter(new FontConverter()); _addConverter(new NumberConverter()); isInitialized = true; } } public static StringEncoder getDefaultInstance() { return defaultInstance; } }