package eis.eis2java.translation; import java.util.AbstractCollection; import java.util.HashMap; import java.util.Iterator; import eis.eis2java.exception.NoTranslatorException; import eis.eis2java.exception.TranslationException; import eis.iilang.Identifier; import eis.iilang.Numeral; import eis.iilang.Parameter; import eis.iilang.ParameterList; import eis.iilang.TruthValue; /** * Singleton class that supports translation from {@link Object} to * {@link Parameter} and vice versa. * * @author Lennard de Rijk */ public class Translator { /** Singleton instance of the translator */ private static Translator singleton; /** * @return Creates or gets the singleton instance of the {@link Translator}. */ public static Translator getInstance() { if (singleton == null) { singleton = new Translator(); } return singleton; } /** * All translators for {@link Object} to {@link Parameter} are located here. */ private final HashMap<Class<?>, Java2Parameter<?>> java2ParameterTranslators; /** * All translators for {@link Parameter} to {@link Object} are located here. */ private final HashMap<Class<?>, Parameter2Java<?>> parameter2JavaTranslators; private Translator() { // Non-instantiable outside of class. java2ParameterTranslators = new HashMap<Class<?>, Java2Parameter<?>>(); parameter2JavaTranslators = new HashMap<Class<?>, Parameter2Java<?>>(); NumberTranslator numberTranslator = new NumberTranslator(); registerJava2ParameterTranslator(numberTranslator); registerParameter2JavaTranslator(numberTranslator); BooleanTranslator booleanTranslator = new BooleanTranslator(); registerJava2ParameterTranslator(booleanTranslator); registerParameter2JavaTranslator(booleanTranslator); CharTranslator charTranslator = new CharTranslator(); registerJava2ParameterTranslator(charTranslator); registerParameter2JavaTranslator(charTranslator); StringTranslator stringTranslator = new StringTranslator(); registerJava2ParameterTranslator(stringTranslator); registerParameter2JavaTranslator(stringTranslator); @SuppressWarnings("rawtypes") CollectionTranslator<?> collectionTranslator = new CollectionTranslator(); registerJava2ParameterTranslator(collectionTranslator); registerParameter2JavaTranslator(new IntegerTranslator()); registerParameter2JavaTranslator(new LongTranslator()); registerParameter2JavaTranslator(new ShortTranslator()); registerParameter2JavaTranslator(new DoubleTranslator()); registerParameter2JavaTranslator(new FloatTranslator()); } /** * Registers a translator for Java to {@link Parameter}. * * @param translator * the translator to register. */ public void registerJava2ParameterTranslator(Java2Parameter<?> translator) { java2ParameterTranslators.put(translator.translatesFrom(), translator); } /** * Registers a translator for {@link Parameter} to Java. * * @param translator * the translator to register. */ public void registerParameter2JavaTranslator(Parameter2Java<?> translator) { parameter2JavaTranslators.put(translator.translatesTo(), translator); } /** * Translates the given object into a {@link Parameter}. The Translator must * contain a translator for the type T or of a superclass of T. Otherwise no * translation can be made. * * @param <T> * The type of the object to translate. * * @param o * the object to translate. * @return The object translated into the parameter. * @throws TranslationException * If the translation could not be made. */ @SuppressWarnings("unchecked") public <T> Parameter[] translate2Parameter(T o) throws TranslationException { Java2Parameter<?> rawTranslator = null; Class<?> clazz = o.getClass(); if (clazz.isPrimitive()) { clazz = getWrapper(clazz); } // Go up the super class tree until we find a class we can translate while (clazz != null && rawTranslator == null) { rawTranslator = java2ParameterTranslators.get(clazz); clazz = clazz.getSuperclass(); } if (rawTranslator == null) { throw new NoTranslatorException(o.getClass()); } Java2Parameter<T> translator = (Java2Parameter<T>) rawTranslator; return translator.translate(o); } /** * Translates the given parameter into an object of type T. The Translator * must contain a translator to type T otherwise no translation can be made. * * @param <T> * The type to translate to. * @param parameter * The parameter to translate. * @param parameterClass * The class to which the parameter should be translated. * @return The parameter translated into the object of class T. * @throws TranslationException * if the translation could not be made. * @throws NoTranslatorException */ @SuppressWarnings("unchecked") public <T> T translate2Java(Parameter parameter, Class<T> parameterClass) throws TranslationException, NoTranslatorException { Class<?> clazz = parameterClass; if (clazz.isPrimitive()) { clazz = getWrapper(clazz); } Parameter2Java<?> rawTranslator = parameter2JavaTranslators.get(clazz); if (rawTranslator == null) { throw new NoTranslatorException(clazz); } Parameter2Java<T> translator = (Parameter2Java<T>) rawTranslator; return translator.translate(parameter); } /** * Returns the class that wraps the given primitive class. * * @param clazz * primitive class to get the wrapper class for. */ private Class<?> getWrapper(Class<?> clazz) { if (Integer.TYPE.equals(clazz)) { return Integer.class; } else if (Boolean.TYPE.equals(clazz)) { return Boolean.class; } else if (Long.TYPE.equals(clazz)) { return Long.class; } else if (Float.TYPE.equals(clazz)) { return Float.class; } else if (Character.TYPE.equals(clazz)) { return Character.class; } else if (Double.TYPE.equals(clazz)) { return Double.class; } else if (Byte.TYPE.equals(clazz)) { return Byte.class; } else if (Short.TYPE.equals(clazz)) { return Short.class; } else if (Void.TYPE.equals(clazz)) { return Void.class; } return null; } /** * Default translator for {@link Number} to {@link Numeral} and vice versa. */ private static class NumberTranslator implements Java2Parameter<Number>, Parameter2Java<Number> { @Override public Parameter[] translate(Number n) throws TranslationException { return new Parameter[] { new Numeral(n) }; } @Override public Class<Number> translatesFrom() { return Number.class; } @Override public Number translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Numeral)) { throw new TranslationException( "Expected a numeral parameter but got " + parameter); } return ((Numeral) parameter).getValue(); } @Override public Class<Number> translatesTo() { return Number.class; } } /** * Default translator for {@link Boolean} to {@link TruthValue} and vice * versa. */ private static class BooleanTranslator implements Java2Parameter<Boolean>, Parameter2Java<Boolean> { @Override public Parameter[] translate(Boolean b) throws TranslationException { return new Parameter[] { new TruthValue(b) }; } @Override public Class<Boolean> translatesFrom() { return Boolean.class; } @Override public Boolean translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof TruthValue)) { throw new TranslationException( "Expected a Truthvalue parameter but got " + parameter); } return ((TruthValue) parameter).getBooleanValue(); } @Override public Class<Boolean> translatesTo() { return Boolean.class; } } /** * Translates a {@link Char} into an {@link Identifier} and vice versa. */ private static class CharTranslator implements Java2Parameter<Character>, Parameter2Java<Character> { @Override public Character translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Identifier)) { throw new TranslationException( "Expected an Identifier parameter but got " + parameter); } Identifier id = (Identifier) parameter; String value = id.getValue(); if (value.length() > 1) { throw new TranslationException( "A single character was expected instead a string of length " + value.length() + " was given. Contents: " + value); } return value.charAt(0); } @Override public Class<Character> translatesTo() { return Character.class; } @Override public Parameter[] translate(Character value) throws TranslationException { return new Parameter[] { new Identifier(String.valueOf(value)) }; } @Override public Class<Character> translatesFrom() { return Character.class; } } /** * Translates a {@link String} into an {@link Identifier} and vice versa. */ private static class StringTranslator implements Java2Parameter<String>, Parameter2Java<String> { @Override public String translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Identifier)) { throw new TranslationException( "Expected an Identifier parameter but got " + parameter); } Identifier id = (Identifier) parameter; return id.getValue(); } @Override public Class<String> translatesTo() { return String.class; } @Override public Parameter[] translate(String value) throws TranslationException { return new Parameter[] { new Identifier(value) }; } @Override public Class<String> translatesFrom() { return String.class; } } /** * Translates a {@link AbstractCollection} into an {@link ParameterList}. */ private static class CollectionTranslator<T> implements Java2Parameter<AbstractCollection<T>> { @Override public Parameter[] translate(AbstractCollection<T> value) throws TranslationException { Parameter[] parameters = new Parameter[value.size()]; int i = 0; Iterator<T> it = value.iterator(); Translator translator = Translator.getInstance(); while (it.hasNext()) { Parameter[] translation = translator.translate2Parameter(it .next()); if (translation.length == 1) { // Translations into single parameters are unwrapped. parameters[i] = translation[0]; } else { parameters[i] = new ParameterList(translation); } i++; } return new Parameter[] { new ParameterList(parameters) }; } @SuppressWarnings("unchecked") @Override public Class<? extends AbstractCollection<T>> translatesFrom() { // mpkorstanje 2012-05-24: When using OpenJDK we can't cast directly // from // Class<AbstractCollection> to Class<? extends // AbstractCollection<T>>. This is a work around. @SuppressWarnings("rawtypes") Class cls = AbstractCollection.class; return (Class<? extends AbstractCollection<T>>) cls; } } /** * Translates {@link Numeral} into an {@link Integer}. */ private static class IntegerTranslator implements Parameter2Java<Integer> { @Override public Integer translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Numeral)) { throw new TranslationException( "Expected a numeral parameter but got " + parameter); } return ((Numeral) parameter).getValue().intValue(); } @Override public Class<Integer> translatesTo() { return Integer.class; } } /** * Translates {@link Numeral} into a {@link Long}. */ private static class LongTranslator implements Parameter2Java<Long> { @Override public Long translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Numeral)) { throw new TranslationException( "Expected a numeral parameter but got " + parameter); } return ((Numeral) parameter).getValue().longValue(); } @Override public Class<Long> translatesTo() { return Long.class; } } /** * Translates {@link Numeral} into a {@link Short}. */ private static class ShortTranslator implements Parameter2Java<Short> { @Override public Short translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Numeral)) { throw new TranslationException( "Expected a numeral parameter but got " + parameter); } return ((Numeral) parameter).getValue().shortValue(); } @Override public Class<Short> translatesTo() { return Short.class; } } /** * Translates {@link Numeral} into a {@link Double}. */ private static class DoubleTranslator implements Parameter2Java<Double> { @Override public Double translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Numeral)) { throw new TranslationException( "Expected a numeral parameter but got " + parameter); } return ((Numeral) parameter).getValue().doubleValue(); } @Override public Class<Double> translatesTo() { return Double.class; } } /** * Translates {@link Numeral} into a {@link Float}. */ private static class FloatTranslator implements Parameter2Java<Float> { @Override public Float translate(Parameter parameter) throws TranslationException { if (!(parameter instanceof Numeral)) { throw new TranslationException( "Expected a numeral parameter but got " + parameter); } return ((Numeral) parameter).getValue().floatValue(); } @Override public Class<Float> translatesTo() { return Float.class; } } }