/* * Rapid Beans Framework: TypeRapidEnum.java * * Copyright (C) 2009 Martin Bluemel * * Creation Date: 11/04/2005 * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; * either version 3 of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copies of the GNU Lesser General Public License and the * GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. */ package org.rapidbeans.core.type; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import org.rapidbeans.core.basic.GenericEnum; import org.rapidbeans.core.basic.RapidEnum; import org.rapidbeans.core.common.RapidBeansLocale; import org.rapidbeans.core.exception.EnumException; import org.rapidbeans.core.exception.TypeNotFoundException; import org.rapidbeans.core.exception.UtilException; import org.rapidbeans.core.util.ClassHelper; import org.rapidbeans.core.util.StringHelper; import org.rapidbeans.core.util.XmlNode; /** * Encapsulates the enum type's information. * * @author Martin Bluemel */ public final class TypeRapidEnum extends RapidBeansType { /** * array with the enum's element instances. */ private List<RapidEnum> elements = null; /** * hash map with the enum's element instances for high performance lookup by * name. */ private Map<String, RapidEnum> enumMap = null; /** * hash map with the enum's element descriptions lookup by name. */ private Map<String, String> descriptionMap = new HashMap<String, String>(); public String getDescription(final String enumElementName) { return this.descriptionMap.get(enumElementName); } public String getDescription(final RapidEnum enumElement) { return this.descriptionMap.get(enumElement.name()); } /** * Return an array with all enum elements of this type. * * @return the enum's element instances. */ public List<RapidEnum> getElements() { int size = this.elements.size(); List<RapidEnum> elemList = new ArrayList<RapidEnum>(size); for (int i = 0; i < size; i++) { elemList.add(this.elements.get(i)); } return elemList; } /** * determine the enum element specified by it's name. * * @param enumElementName * the name of the enum element * * @return the enum Element specified by it's name * * @throws org.rapidbeans.core.exception.EnumException * if an element with the given name has not been found. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public RapidEnum elementOf(final String enumElementName) { RapidEnum e = null; if (this.getImplementingClass() != null) { try { e = (RapidEnum) java.lang.Enum.valueOf((Class) this.getImplementingClass(), enumElementName); } catch (final IllegalArgumentException ex) { if (this.enumMap != null) { e = this.enumMap.get(enumElementName); } } } else { e = this.enumMap.get(enumElementName); } if (e == null) { if (!this.getName().equals("org.rapidbeans.core.basic.RapidEnum")) { throw new EnumException("element \"" + enumElementName + "\" is not part of enum \"" + this.getName() + "\""); } } return e; } /** * determine the enum element with the given index. * * @param index * the index of the enum element to retrieve. * * @return the enum element with the given index. */ public RapidEnum elementOf(final int index) { return this.elements.get(index); } /** * determine the index of a certain enum element specified by it's name. * * @param enumElementName * the name of the enum element * * @return the index of the enum Element specified by it's name */ public int indexOf(final String enumElementName) { return this.elementOf(enumElementName).ordinal(); } /** * Check RapidEnum type compatibility. * * @param otherType * the other RapidEnum type to compare with * @return if the types are compatible */ public boolean isAssignableFrom(TypeRapidEnum otherType) { // currently since we do not have the concept of enum inheritance // only equal types are compatible return this == otherType; } /** * Retrieve the localized name of the enum element * * @param locale * the locale * * @return a string for the property's value for UI */ public String getStringGui(final RapidEnum enumElement, final RapidBeansLocale locale) { try { return locale.getStringGui("enum." + this.getName().toLowerCase() + "." + enumElement.name().toLowerCase()); } catch (MissingResourceException e) { return enumElement.name(); } } /** * Retrieve the short form localized name of the enum element * * @param locale * the locale * * @return a string for the property's value for UI */ public String getStringGuiShort(final RapidEnum enumElement, final RapidBeansLocale locale) { return this.getStringGui(enumElement, locale).toUpperCase().substring(0, 2); } /** * format a comma separated String out of a list with enum elements. * * @param enumList * the List with enum elements * @return the formatted String */ public static String format(final List<RapidEnum> enumList) { StringBuffer s = new StringBuffer(); final int size = enumList.size(); if (size >= 1) { s.append(enumList.get(0).name()); } for (int i = 1; i < size; i++) { s.append(','); s.append(enumList.get(i).name()); } return s.toString(); } /** * parse a comma separated String and convert into a list of enum elements. * * @param descr * the String to parse * * @return the list of enum elements */ public ArrayList<RapidEnum> parse(final String descr) { final ArrayList<RapidEnum> enumList = new ArrayList<RapidEnum>(); for (final String currentName : StringHelper.split(descr, ",")) { enumList.add(this.elementOf(currentName)); } return enumList; } /** * initialize an enum type out of it's (generated) class. * * @param clazz * the real enum class * * @return the enum's type instance */ public static TypeRapidEnum createInstance(final Class<?> clazz) { if (!ClassHelper.classOf(RapidEnum.class, clazz)) { throw new UtilException("tried to init an enum with a class that is not subclass of \"BBIEnum\""); } final TypeRapidEnum type = new TypeRapidEnum(clazz); RapidBeansTypeLoader.getInstance().registerType(type); return type; } /** * initialize a real generic enum type out of it's description. * * @param descr * the typename * @param elements * the elements * * @return the enum type instance */ public static TypeRapidEnum createInstance(final String typename, final List<RapidEnum> elements) { final TypeRapidEnum type = new TypeRapidEnum(typename, elements); RapidBeansTypeLoader.getInstance().registerType(type); return type; } /** * initialize a real generic enum type out of it's description. * * @param descr * the typename * * @return the enum type instance */ public static TypeRapidEnum createInstance(final String typename) { final TypeRapidEnum type = new TypeRapidEnum(typename); RapidBeansTypeLoader.getInstance().registerType(type); return type; } /** * constructor for a type instance of a generic enum. * * @param typename * the fully qualified enum type name */ public TypeRapidEnum(final String typename) { XmlNode xmlDeclaration = null; try { xmlDeclaration = RapidBeansType.loadDescription(typename); setDescription(RapidBeansType.readDescription(xmlDeclaration)); } catch (TypeNotFoundException e) { xmlDeclaration = null; } try { final Class<?> clazz = Class.forName(typename); this.initFromClass(clazz, xmlDeclaration); } catch (ClassNotFoundException e) { this.initFromTypeDescr(typename, xmlDeclaration); } } /** * constructor for a type instance of a generic enum with elements already * given. * * @param typename * the fully qualified enum type name * @param enumElements * the enum elements */ public TypeRapidEnum(final String typename, final List<RapidEnum> enumElements) { XmlNode xmlDeclaration = null; try { xmlDeclaration = RapidBeansType.loadDescription(typename); setDescription(RapidBeansType.readDescription(xmlDeclaration)); } catch (TypeNotFoundException e) { xmlDeclaration = null; } this.setName(typename); Class<?> clazz = null; try { clazz = Class.forName(typename); } catch (ClassNotFoundException e) { clazz = null; } this.setImplementingClass(clazz); this.elements = enumElements; this.enumMap = new Hashtable<String, RapidEnum>(); for (RapidEnum elem : this.elements) { this.enumMap.put(elem.name(), elem); } this.checkElements(); } /** * constructor. * * @param clazz * the real enum class */ public TypeRapidEnum(final Class<?> clazz) { XmlNode xmlDeclaration = null; try { xmlDeclaration = RapidBeansType.loadDescription(clazz.getName()); setDescription(RapidBeansType.readDescription(xmlDeclaration)); } catch (TypeNotFoundException e) { xmlDeclaration = null; } this.initFromClass(clazz, xmlDeclaration); } /** * constructor for a type instance of a generic enumeration. * * @param typename * the fully qualified enumeration type name * @param decl * the enumeration type declaration */ private void initFromClass(final Class<?> clazz, final XmlNode decl) { this.setName(clazz.getName()); this.setImplementingClass(clazz); // explore the enum type via reflection Field[] fields = clazz.getDeclaredFields(); Object obj; RapidEnum enumelem = null; this.elements = new ArrayList<RapidEnum>(); for (int i = 0; i < fields.length; i++) { try { obj = fields[i].get(enumelem); if (obj.getClass() == clazz) { enumelem = (RapidEnum) obj; final XmlNode enumNode = findEnumNode(decl, enumelem.name()); this.descriptionMap.put(enumelem.name(), RapidBeansType.readDescription(enumNode)); this.elements.add(enumelem); } } catch (IllegalAccessException e) { // just do nothing in case of non accessible members obj = null; } } this.checkElements(); } private XmlNode findEnumNode(final XmlNode decl, final String name) { for (final XmlNode node : decl.getSubnodes("enum")) { if (node.getAttributeValue("@name").equals(name)) { return node; } } return null; } /** * constructor for a type instance of a generic enum. * * @param typenameOrDescr * the fully qualified enum type name or the whole description */ private void initFromTypeDescr(final String typenameOrDescr, final XmlNode decl) { this.setImplementingClass(null); this.elements = new ArrayList<RapidEnum>(); this.enumMap = new Hashtable<String, RapidEnum>(); XmlNode tlNode = null; if (typenameOrDescr.startsWith("<enumtype")) { tlNode = XmlNode.getDocumentTopLevel(typenameOrDescr); this.setName(tlNode.getAttributeValue("@name")); } else { tlNode = RapidBeansType.loadDescription(typenameOrDescr); this.setName(typenameOrDescr); } int i = 0; for (XmlNode node : tlNode.getSubnodes("enum")) { this.descriptionMap.put(node.getAttributeValue("@name"), RapidBeansType.readDescription(node)); final GenericEnum genericEnum = new GenericEnum(this, node.getAttributeValue("@name"), i++); this.elements.add(genericEnum); this.enumMap.put(genericEnum.name(), genericEnum); } this.checkElements(); } private void checkElements() { if (this.elements.size() == 0) { if (!this.getName().equals("org.rapidbeans.core.basic.RapidEnum")) { throw new EnumException("Problems with enumeration type \"" + getName() + "\". An enum must at least have one member"); } } int j; final int elemSize = this.elements.size(); for (int i = 0; i < elemSize; i++) { final RapidEnum enumElement = this.elements.get(i); for (j = i + 1; j < elemSize; j++) { if (enumElement == this.elements.get(j)) { throw new EnumException("Invalid enum for TypeRapidEnum (enum class name \"" + this.getName() + "\"): enum element \"" + this.elements.get(i).name() + "\" enumerated more than once\n"); } } } } /** * find an enum type out of a name. * <p> * <b>throws TypeNotFoundException:</b><br/> * if the specified enum type does not exist<br/> * </p> * * @param typename * the type's name * * @return null if the enum with the specified type name could not be found * and a concrete enum class could not be loaded */ public static TypeRapidEnum forName(final String typename) { return (TypeRapidEnum) RapidBeansTypeLoader.getInstance().loadType(TypeRapidEnum.class, typename); } }