package humanize.text; import humanize.Humanize; import humanize.spi.Expose; import humanize.spi.FormatProvider; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.FieldPosition; import java.text.Format; import java.text.ParsePosition; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import com.google.common.base.Preconditions; /** * <p> * Exposes as {@link Format}s all the methods annotated with {@link Expose} of * {@link Humanize} class. * </p> * * Example: * * <pre> * Humanize.format("size {0, humanize, binary.prefix}", 8); * // == "size 8 bytes" * </pre> */ public class HumanizeFormatProvider implements FormatProvider { public static class HumanizeFormat extends Format { private static final long serialVersionUID = -3261072590121741805L; private final Locale locale; private final SerializableMethod method; public HumanizeFormat(Method method, Locale locale) { this.method = new SerializableMethod(method); this.locale = locale; } @Override public StringBuffer format(Object paramObject, StringBuffer toAppendTo, FieldPosition position) { Preconditions.checkNotNull(method); Class<?>[] paramTypes = method.getParameterTypes(); boolean withLocale = false; Object retval = null; for (Class<?> type : paramTypes) { if (Locale.class.equals(type)) { withLocale = true; break; } } try { retval = withLocale ? method.invoke(null, paramObject, locale) : method.invoke(null, paramObject); } catch (Exception e) { retval = String.format("[invalid call: '%s']", e.getMessage()); } return toAppendTo.append(retval); } @Override public Object parseObject(String paramString, ParsePosition paramParsePosition) { throw new UnsupportedOperationException(); } } private static class SerializableMethod implements Serializable { private static final long serialVersionUID = 3407738033068323298L; private Method method; public SerializableMethod(Method method) { this.method = method; } public Class<?>[] getParameterTypes() { return method.getParameterTypes(); } public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { return method.invoke(obj, args); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { Class<?> declaringClass = (Class<?>) in.readObject(); String methodName = in.readUTF(); Class<?>[] parameterTypes = (Class<?>[]) in.readObject(); try { method = declaringClass.getMethod(methodName, parameterTypes); } catch (Exception e) { throw new IOException(String.format("Error occurred resolving deserialized method '%s.%s'", declaringClass.getSimpleName(), methodName), e); } } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(method.getDeclaringClass()); out.writeUTF(method.getName()); out.writeObject(method.getParameterTypes()); } } private static final Map<String, Method> humanizeMethods = getStaticMethods(Humanize.class); public static FormatFactory factory() { return new FormatFactory() { @Override public Format getFormat(String name, String args, Locale locale) { String camelized = Humanize.camelize(args); if (humanizeMethods.containsKey(camelized)) { Method method = humanizeMethods.get(camelized); return new HumanizeFormat(method, locale); } // not found return null; } }; } private static Map<String, Method> getStaticMethods(Class<?> clazz) { Map<String, Method> methods = new HashMap<String, Method>(); for (Method method : clazz.getMethods()) { if (Modifier.isStatic(method.getModifiers()) && method.getAnnotation(Expose.class) != null) { methods.put(method.getName(), method); } } return Collections.unmodifiableMap(methods); } @Override public FormatFactory getFactory() { return factory(); } @Override public String getFormatName() { return "humanize"; } }