/* * Scriptographer * * This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator * http://scriptographer.org/ * * Copyright (c) 2002-2010, Juerg Lehni * http://scratchdisk.com/ * * All rights reserved. See LICENSE file for details. * * File created on Feb 11, 2008. */ package com.scratchdisk.script; import java.lang.reflect.Constructor; import java.util.IdentityHashMap; import com.scratchdisk.util.ClassUtils; import com.scratchdisk.util.ConversionUtils; import com.scriptographer.script.EnumUtils; /** * @author lehni * */ public abstract class ArgumentReader { protected Converter converter; public ArgumentReader(Converter converter) { this.converter = converter; } protected abstract Object readNext(String name); public boolean has(String name) { return false; } public int size() { return -1; } public Object[] keys() { return new Object[]{}; } public boolean isArray() { return false; } public boolean isString() { return false; } public boolean isMap() { return false; } public void revert() { // Do nothing here. For sequentially reading readers go back one, // in order to try a different type... } public Boolean readBoolean(String name) { Object obj = readNext(name); return obj != null ? new Boolean(ConversionUtils.toBoolean(obj)) : null; } public boolean readBoolean(String name, boolean defaultValue) { Boolean value = readBoolean(name); return value != null ? value.booleanValue() : defaultValue; } public boolean readBoolean(boolean defaultValue) { return readBoolean(null, defaultValue); } public Double readDouble(String name) { Object obj = readNext(name); return obj != null ? new Double(ConversionUtils.toDouble(obj)) : null; } public Double readDouble() { return readDouble(null); } public double readDouble(String name, double defaultValue) { return ConversionUtils.toDouble(readNext(name), defaultValue); } public double readDouble(double defaultValue) { return readDouble(null, defaultValue); } public Float readFloat(String name) { Object obj = readNext(name); return obj != null ? new Float(ConversionUtils.toFloat(obj)) : null; } public Float readFloat() { return readFloat(null); } public float readFloat(String name, float defaultValue) { return ConversionUtils.toFloat(readNext(name), defaultValue); } public float readFloat(float defaultValue) { return readFloat(null, defaultValue); } public Integer readInteger(String name) { Object obj = readNext(name); return obj != null ? new Integer(ConversionUtils.toInt(obj)) : null; } public Integer readInteger() { return readInteger(null); } public int readInteger(String name, int defaultValue) { return ConversionUtils.toInt(readNext(name), defaultValue); } public int readInteger(int defaultValue) { return readInteger(null, defaultValue); } public String readString(String name) { return readString(name, null); } public String readString() { return readString(null); } public String readString(String name, String defaultValue) { return ConversionUtils.toString(readNext(name), defaultValue); } protected static IdentityHashMap<Class, ArgumentConverter> converters = new IdentityHashMap<Class, ArgumentConverter>(); protected static void registerConverter(Class type, ArgumentConverter converter) { converters.put(type, converter); } @SuppressWarnings("unchecked") protected static <T> ArgumentConverter<T> getConverter(Class<T> type) { return converters.get(type); } @SuppressWarnings("unchecked") public <T> T readObject(String name, Class<T> type) { Object obj = readNext(name); if (obj != null) { if (type.isInstance(obj)) return (T) obj; ArgumentConverter<T> converter = getConverter(type); T res; if (converter != null) { // Make a new ArgumentReader for this object first, // using convert: ArgumentReader reader = this.converter.convert(obj, ArgumentReader.class); if (reader == null) throw new IllegalArgumentException("Cannot read from " + obj); res = converter.convert(reader, this.converter.unwrap(obj)); } else { try { res = this.converter.convert(obj, type); } catch (Exception e) { // TODO: report? res = null; } } return res; } return null; } public <T> T readObject(Class<T> type) { return readObject(null, type); } public Object readObject(String name) { return readObject(name, Object.class); } public Object readObject() { return readObject(Object.class); } public <T extends Enum<T>> T readEnum(String name, Class<T> type, T defaultValue) { T value = EnumUtils.get(type, readString(name)); return value != null ? value : defaultValue; } public <T extends Enum<T>> T readEnum(String name, Class<T> type) { return readEnum(name, type, null); } public <T extends Enum<T>> T readEnum(Class<T> type) { return readEnum(null, type); } public <T extends Enum<T>> T readEnum(Class<T> type, T defaultValue) { return readEnum(null, type, defaultValue); } public static boolean canConvert(Class to) { return ArgumentReader.class.isAssignableFrom(to) || getArgumentReaderConstructor(to) != null || converters.get(to) != null; } public static Object convert(ArgumentReader reader, Object from, Class<?> to, Converter converter) { if (ArgumentReader.class.isAssignableFrom(to)) return reader; ArgumentConverter argumentConverter = converters.get(to); if (argumentConverter != null) { Object result = argumentConverter.convert(reader, from); // ArgumentConverter can return another convertible type, to be // passed forward to the Converter. This is used e.g. for // java.awt.Color <-> com.scriptographer.script.ColorConverter which // returns com.scriptographer.ai.Color... if (to.isInstance(result)) return result; else if (converter != null) return converter.convert(result, to); } else { Constructor ctor = getArgumentReaderConstructor(to); if (ctor != null) { // Create an object using the rgumentReader constructor. // Argument readers can either be created from a NativeArray or // a Scriptable object try { return ctor.newInstance(new Object[] { reader }); } catch (Exception e) { e.printStackTrace(); } } } return null; } /** * Determines whether the class has a constructor taking a single map as * argument or not. * A cache is used to speed up lookup. * * @param type * @return true if the class has a map constructor, false otherwise. */ private static Constructor getArgumentReaderConstructor(Class type) { return ClassUtils.getConstructor(type, new Class[] { ArgumentReader.class }, argumentReaderConstructors); } private static IdentityHashMap<Class, Constructor> argumentReaderConstructors = new IdentityHashMap<Class, Constructor>(); public void setProperties(Object object) { converter.setProperties(object, this); } }