package knorxx.framework.generator.web.generator; import com.google.common.collect.Sets; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import knorxx.framework.generator.JavaFileWithSource; import knorxx.framework.generator.single.JavaScriptResult; import knorxx.framework.generator.single.SingleFileGeneratorException; import knorxx.framework.generator.single.SingleResult; import knorxx.framework.generator.util.JavaIdentifierUtils; import knorxx.framework.generator.web.generator.stjs.StjsJavaScriptClassBuilder; import org.apache.commons.lang3.text.WordUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; /** * This special enum generator emulates the st-js enum generation with one important exception: It does not need the * source of the enum class to be available. This allows easy usage of enums from other JARs. * * @author sj */ public class EnumGenerator extends SpecialFileGenerator { @Override public SingleResult generate(Class<?> javaClass) throws SingleFileGeneratorException { List<String> constants = new ArrayList<>(); for (Enum constant : ((Class<Enum<?>>) javaClass).getEnumConstants()) { constants.add(constant.name()); } StjsJavaScriptClassBuilder builder = new StjsJavaScriptClassBuilder(javaClass) .enumeration(javaClass, constants.toArray(new String[]{})).newLine(); Map<String, Object> enumConstValueGetters = getEnumConstValueGetters((Class<Enum<?>>) javaClass); for(String constantAndGetterName : enumConstValueGetters.keySet()) { builder.staticFunction(javaClass, constantAndGetterName) .code("return ").literal(enumConstValueGetters.get(constantAndGetterName)).semicolon() ._function(); } return new JavaScriptResult(builder.create()); } @Override public boolean isGeneratable(Class<?> javaClass) { return JavaIdentifierUtils.hasSuperclassOrImplementsInterface(javaClass, Enum.class.getName()); } private Map<String, Object> getEnumConstValueGetters(Class<? extends Enum> enumClass) { Map<String, Object> fieldGetterMap = new HashMap<>(); Set<String> internalNames = Sets.newHashSet("$VALUES"); for (Enum constant : enumClass.getEnumConstants()) { internalNames.add(constant.name()); } List<Pair<Field, String>> fieldsAndGetters = new ArrayList<>(); for (Field field : enumClass.getDeclaredFields()) { if (!internalNames.contains(field.getName())) { String getterName = "get" + WordUtils.capitalize(field.getName(), '\0'); if (hasGetter(enumClass, getterName)) { fieldsAndGetters.add(new ImmutablePair<>(field, getterName)); } } } for (Pair<Field, String> fieldAndGetter : fieldsAndGetters) { for (Enum constant : enumClass.getEnumConstants()) { fieldGetterMap.put(constant.name() + "." + fieldAndGetter.getRight(), getValue(fieldAndGetter.getLeft(), constant)); } } return fieldGetterMap; } private Object getValue(Field field, Enum constant) { try { field.setAccessible(true); return field.get(constant); } catch (IllegalArgumentException | IllegalAccessException ex) { throw new IllegalStateException("An error occured while getting the value of the field '" + field.getName() + "' of the enum constant '" + constant.name() + "'!", ex); } } private boolean hasGetter(Class<? extends Enum> enumClass, String getterName) { try { enumClass.getMethod(getterName); return true; } catch (NoSuchMethodException ex) { return false; } catch (SecurityException ex) { throw new IllegalStateException("An error occured while searching for a getter named '" + getterName + "' in the class '" + enumClass.getName() + "'!", ex); } } }