/* * @(#)JavaPrimitivesDOMFactory.java * * Copyright (c) 2009-2010 The authors and contributors of JHotDraw. * * You may not use, copy or modify this file, except in compliance with the * accompanying license terms. */ package org.jhotdraw.xml; import edu.umd.cs.findbugs.annotations.Nullable; import java.awt.Color; import java.awt.Font; import java.io.IOException; import java.util.regex.Matcher; /** * {@code JavaPrimitivesDOMFactory} can be used to serialize Java primitive objects * and {@link DOMStorable} objects. * <p> * The following Java primitive types are supported. Object wrappers are * automatically unwrapped into their primitive types. * <ul> * <li>null</li> * <li>boolean</li> * <li>byte</li> * <li>short</li> * <li>char</li> * <li>int</li> * <li>long</li> * <li>float</li> * <li>double</li> * <li>string</li> * <li>enum</li> * <li>color (will be removed in a future revision of this class!)</li> * <li>font (will be removed in a future revision of this class!)</li> * </ul> * Arrays of primitive types are supported, by appending the word "Array" to * a primitive type name. * <p> * You can add support for additional primitive types by overriding the methods * {@code read} and {@code write}. * <p> * In addition to the primitive types, this factory can store and read * {@link DOMStorable} objects. No mapping for {@link DOMStorable} class names is performed. * For example, if a {@link DOMStorable} object has the class name {@code com.example.MyClass}, * then the DOM element has the same name, that is: {@code <com.example.MyClass>}. * <p> * Since no mapping between DOM element names and {@link DOMStorable} class names is performed, * DOM's generated with JavaPrimitivesDOMFactory are not suited for long-term * storage of objects. This is because a DOM element can not be read back into an * object, if the class name of the object has changed. * <p> * You can implement a mapping by overriding the methods {@code getName}, * {@code create}, {@code getEnumName} and {@code getEnumValue}. * * @author Werner Randelshofer * @version $Id$ */ public class JavaPrimitivesDOMFactory implements DOMFactory { private String escape(String name) { // Escape dollar characters by two full-stop characters name = name.replaceAll("\\$", ".."); return name; } private String unescape(String name) { // Unescape dollar characters from two full-stop characters name = name.replaceAll("\\.\\.", Matcher.quoteReplacement("$")); return name; } @Override public String getName(Object o) { if (o == null) { return "null"; } else if (o instanceof Boolean) { return "boolean"; } else if (o instanceof Byte) { return "byte"; } else if (o instanceof Character) { return "char"; } else if (o instanceof Short) { return "short"; } else if (o instanceof Integer) { return "int"; } else if (o instanceof Long) { return "long"; } else if (o instanceof Float) { return "float"; } else if (o instanceof Double) { return "double"; } else if (o instanceof Color) { return "color"; } else if (o instanceof Font) { return "font"; } else if (o instanceof byte[]) { return "byteArray"; } else if (o instanceof char[]) { return "charArray"; } else if (o instanceof short[]) { return "shortArray"; } else if (o instanceof int[]) { return "intArray"; } else if (o instanceof long[]) { return "longArray"; } else if (o instanceof float[]) { return "floatArray"; } else if (o instanceof double[]) { return "doubleArray"; } else if (o instanceof String) { return "string"; } else if (o instanceof Enum) { return "enum"; } else if (o instanceof Color) { return "color"; } else if (o instanceof Font) { return "font"; } return escape(o.getClass().getName()); } @Override public Object create(String name) { name = unescape(name); try { return Class.forName(name).newInstance(); } catch (InstantiationException ex) { IllegalArgumentException e = new IllegalArgumentException("Class " + name + " can not instantiate an object"); e.initCause(ex); throw e; } catch (IllegalAccessException ex) { IllegalArgumentException e = new IllegalArgumentException("Class " + name + " is not public"); e.initCause(ex); throw e; } catch (ClassNotFoundException ex) { IllegalArgumentException e = new IllegalArgumentException("Class " + name + " does not exist"); e.initCause(ex); throw e; } } protected String getEnumName(Enum<?> o) { return escape(o.getClass().getName()); } protected String getEnumValue(Enum<?> o) { return o.name(); } @SuppressWarnings("unchecked") protected <E extends Enum<E>> Enum<E> createEnum(String name, String value) { name = unescape(name); Class<E> enumClass; try { enumClass = (Class<E>) Class.forName(name); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException("Class not found for Enum with name:" + name); } if (enumClass == null) { throw new IllegalArgumentException("Enum name not known to factory:" + name); } return Enum.valueOf(enumClass, value); } @Override public void write(DOMOutput out, Object o) throws IOException { if (o == null) { // nothing to do } else if (o instanceof DOMStorable) { ((DOMStorable) o).write(out); } else if (o instanceof String) { out.addText((String) o); } else if (o instanceof Integer) { out.addText(o.toString()); } else if (o instanceof Long) { out.addText(o.toString()); } else if (o instanceof Double) { out.addText(o.toString()); } else if (o instanceof Float) { out.addText(o.toString()); } else if (o instanceof Boolean) { out.addText(o.toString()); } else if (o instanceof Color) { Color c = (Color) o; out.addAttribute("rgba", "#" + Integer.toHexString(c.getRGB())); } else if (o instanceof byte[]) { byte[] a = (byte[]) o; for (int i = 0; i < a.length; i++) { out.openElement("byte"); write(out, a[i]); out.closeElement(); } } else if (o instanceof boolean[]) { boolean[] a = (boolean[]) o; for (int i = 0; i < a.length; i++) { out.openElement("boolean"); write(out, a[i]); out.closeElement(); } } else if (o instanceof char[]) { char[] a = (char[]) o; for (int i = 0; i < a.length; i++) { out.openElement("char"); write(out, a[i]); out.closeElement(); } } else if (o instanceof short[]) { short[] a = (short[]) o; for (int i = 0; i < a.length; i++) { out.openElement("short"); write(out, a[i]); out.closeElement(); } } else if (o instanceof int[]) { int[] a = (int[]) o; for (int i = 0; i < a.length; i++) { out.openElement("int"); write(out, a[i]); out.closeElement(); } } else if (o instanceof long[]) { long[] a = (long[]) o; for (int i = 0; i < a.length; i++) { out.openElement("long"); write(out, a[i]); out.closeElement(); } } else if (o instanceof float[]) { float[] a = (float[]) o; for (int i = 0; i < a.length; i++) { out.openElement("float"); write(out, a[i]); out.closeElement(); } } else if (o instanceof double[]) { double[] a = (double[]) o; for (int i = 0; i < a.length; i++) { out.openElement("double"); write(out, a[i]); out.closeElement(); } } else if (o instanceof Font) { Font f = (Font) o; out.addAttribute("name", f.getName()); out.addAttribute("style", f.getStyle()); out.addAttribute("size", f.getSize()); } else if (o instanceof Enum) { Enum<?> e = (Enum<?>) o; out.addAttribute("type", getEnumName(e)); out.addText(getEnumValue(e)); } else { throw new IllegalArgumentException("Unsupported object type:" + o); } } @Override @Nullable public Object read(DOMInput in) throws IOException { Object o; String tagName = in.getTagName(); if ("null".equals(tagName)) { o = null; } else if ("boolean".equals(tagName)) { o = Boolean.valueOf(in.getText()); } else if ("byte".equals(tagName)) { o = Byte.decode(in.getText()); } else if ("short".equals(tagName)) { o = Short.decode(in.getText()); } else if ("int".equals(tagName)) { o = Integer.decode(in.getText()); } else if ("long".equals(tagName)) { o = Long.decode(in.getText()); } else if ("float".equals(tagName)) { o = new Float(Float.parseFloat(in.getText())); } else if ("double".equals(tagName)) { o = new Double(Double.parseDouble(in.getText())); } else if ("string".equals(tagName)) { o = in.getText(); } else if ("enum".equals(tagName)) { o = createEnum(in.getAttribute("type", (String) null), in.getText()); } else if ("color".equals(tagName)) { o = new Color(in.getAttribute("rgba", 0xff)); } else if ("font".equals(tagName)) { o = new Font(in.getAttribute("name", "Dialog"), in.getAttribute("style", 0), in.getAttribute("size", 0)); } else if ("byteArray".equals(tagName)) { byte[] a = new byte[in.getElementCount()]; for (int i = 0; i < a.length; i++) { a[i] = ((Byte) in.readObject(i)).byteValue(); } o = a; } else if ("shortArray".equals(tagName)) { short[] a = new short[in.getElementCount()]; for (int i = 0; i < a.length; i++) { a[i] = ((Short) in.readObject(i)).shortValue(); } o = a; } else if ("intArray".equals(tagName)) { int[] a = new int[in.getElementCount()]; for (int i = 0; i < a.length; i++) { a[i] = ((Integer) in.readObject(i)).intValue(); } o = a; } else if ("longArray".equals(tagName)) { long[] a = new long[in.getElementCount()]; for (int i = 0; i < a.length; i++) { a[i] = ((Long) in.readObject(i)).longValue(); } o = a; } else if ("floatArray".equals(tagName)) { float[] a = new float[in.getElementCount()]; for (int i = 0; i < a.length; i++) { a[i] = ((Float) in.readObject(i)).floatValue(); } o = a; } else if ("doubleArray".equals(tagName)) { double[] a = new double[in.getElementCount()]; for (int i = 0; i < a.length; i++) { a[i] = ((Double) in.readObject(i)).doubleValue(); } o = a; } else { o = create(in.getTagName()); } return o; } }