/** * */ package ecologylab.serialization; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.w3c.dom.Node; import ecologylab.generic.HashMapArrayList; import ecologylab.generic.ReflectionTools; import ecologylab.generic.StringBuilderBaseUtils; import ecologylab.generic.StringTools; import ecologylab.platformspecifics.FundamentalPlatformSpecifics; import ecologylab.serialization.MetaInformation.Argument; import ecologylab.serialization.annotations.Hint; import ecologylab.serialization.annotations.simpl_classes; import ecologylab.serialization.annotations.simpl_collection; import ecologylab.serialization.annotations.simpl_composite; import ecologylab.serialization.annotations.simpl_filter; import ecologylab.serialization.annotations.simpl_hints; import ecologylab.serialization.annotations.simpl_inherit; import ecologylab.serialization.annotations.simpl_map; import ecologylab.serialization.annotations.simpl_map_key_field; import ecologylab.serialization.annotations.simpl_nowrap; import ecologylab.serialization.annotations.simpl_other_tags; import ecologylab.serialization.annotations.simpl_scalar; import ecologylab.serialization.annotations.simpl_scope; import ecologylab.serialization.annotations.simpl_tag; import ecologylab.serialization.annotations.simpl_wrap; import ecologylab.serialization.formatenums.Format; import ecologylab.serialization.library.html.A; import ecologylab.serialization.library.html.Div; import ecologylab.serialization.library.html.Input; import ecologylab.serialization.library.html.Td; import ecologylab.serialization.library.html.Tr; import ecologylab.serialization.simplstringformats.FormatRegistry; import ecologylab.serialization.types.CollectionType; import ecologylab.serialization.types.FundamentalTypes; import ecologylab.serialization.types.ScalarType; import ecologylab.serialization.types.TypeRegistry; import ecologylab.serialization.types.element.IMappable; /** * Used to provide convenient access for setting and getting values, using the * ecologylab.serialization type system. Provides marshalling and unmarshalling from Strings. * * @author andruid */ @SuppressWarnings("rawtypes") @simpl_inherit public class FieldDescriptor extends DescriptorBase implements IMappable<String>, Cloneable { public static final String NULL = ScalarType.DEFAULT_VALUE_STRING; public static final Class[] SET_METHOD_STRING_ARG = { String.class }; @simpl_scalar protected Field field; // TODO /** * For nested elements, and collections or maps of nested elements. The class descriptor */ @simpl_composite private ClassDescriptor elementClassDescriptor; @simpl_scalar private String mapKeyFieldName; @simpl_scalar /** * Represents the simple name of the field type. */ private String fieldTypeSimpleName; /** * Descriptor for the class that this field is declared in. */ @simpl_composite protected ClassDescriptor declaringClassDescriptor; @simpl_scalar private Class elementClass; @simpl_scalar protected boolean isGeneric; /** * For composite or collection fields declared with generic type variables, this field stores the * binding to the resolved generic type from the ClassDescriptor. * <p/> * Note: this will require cloning this during inheritance, when subtypes instantiate the generic * type var(s) with different values. */ @simpl_collection("generic_type_var") private ArrayList<GenericTypeVar> genericTypeVars; ClassDescriptor genericTypeVarsContextCD; // ///////////////// next fields are for polymorphic fields // //////////////////////////////////////// /** * Null if the tag for this field is derived from its field declaration. For most fields, tag is * derived from the field declaration (using field name or @simpl_tag). * <p/> * However, for polymorphic fields, such as those declared using @xml_class, @xml_classes, or * * @xml_scope, the tag is derived from the class declaration (using class name or @simpl_tag). * This is, for example, required for polymorphic nested and collection fields. For * these fields, this slot contains an array of the legal classes, which will be bound * to this field during translateFromXML(). */ @simpl_map("polymorph_class_descriptor") @simpl_map_key_field("tagName") private HashMapArrayList<String, ClassDescriptor> polymorphClassDescriptors; @simpl_map("polymorph_class") private HashMap<String, Class> polymorphClasses; @Deprecated // we now use the package name to infer namespaces. @simpl_map("library_namespace") private HashMap<String, String> libraryNamespaces = new HashMap<String, String>(); @simpl_scalar private FieldType type; /** * This slot makes sense only for attributes and leaf nodes */ @simpl_scalar private ScalarType<?> scalarType; @simpl_scalar private CollectionType collectionType; @simpl_scalar private Hint xmlHint; @simpl_scalar private boolean isEnum; private EnumerationDescriptor enumDescriptor; /** * An option for scalar formatting. */ private String[] format; @simpl_scalar private boolean isCDATA; @simpl_scalar private boolean needsEscaping; @simpl_scalar Pattern filterRegex; @simpl_scalar int filterGroup; @simpl_scalar String filterReplace; /** * The FieldDescriptor for the field in a wrap. */ private FieldDescriptor wrappedFD; private FieldDescriptor wrapper; private HashMap<Integer, ClassDescriptor> tlvClassDescriptors; private String unresolvedScopeAnnotation = null; private Class[] unresolvedClassesAnnotation = null; /** * */ @simpl_scalar private String collectionOrMapTagName; @simpl_scalar private String compositeTagName; /** * Used for Collection and Map fields. Tells if the XML should be wrapped by an intermediate * element. */ @simpl_scalar private boolean wrapped; private Method setValueMethod; private String bibtexTag = ""; private boolean isBibtexKey = false; @simpl_scalar private String fieldType; protected String genericParametersString; private ArrayList<ClassDescriptor> dependencies = new ArrayList<ClassDescriptor>(); /** * if is null, this field is not a cloned one. <br /> * if not null, refers to the descriptor that this field is cloned from. */ private FieldDescriptor clonedFrom; /** * Default constructor only for use by translateFromXML(). */ public FieldDescriptor() { super(); } /** * Constructor for the pseudo-FieldDescriptor associated with each ClassDesctiptor, for * translateToXML of fields that deriveTagFromClass. * * @param baseClassDescriptor */ public FieldDescriptor(ClassDescriptor baseClassDescriptor) { super(baseClassDescriptor.getTagName(), null); this.declaringClassDescriptor = baseClassDescriptor; this.field = null; this.type = FieldType.PSEUDO_FIELD_DESCRIPTOR; this.scalarType = null; this.bibtexTag = baseClassDescriptor.getBibtexType(); } /** * Constructor for wrapper FieldDescriptor. (Seems to not have a name; i guess the constituent * field inside the wrapper is where the name is.) * * @param baseClassDescriptor * @param wrappedFD * @param wrapperTag */ public FieldDescriptor(ClassDescriptor baseClassDescriptor, FieldDescriptor wrappedFD, String wrapperTag) { super(wrapperTag, null); this.declaringClassDescriptor = baseClassDescriptor; this.wrappedFD = wrappedFD; wrappedFD.wrapper = this; this.setType(FieldType.WRAPPER); } /** * This is the normal constructor. * * @param declaringClassDescriptor * @param field * @param annotationType * Coarse pre-evaluation of the field's annotation type. Does not differentiate scalars * from elements, or check for semantic consistency. */ public FieldDescriptor(ClassDescriptor declaringClassDescriptor, Field field, FieldType annotationType) // String // nameSpacePrefix { super(XMLTools.getXmlTagName(field), field.getName()); // uses field name or @simpl_tag // declaration this.declaringClassDescriptor = declaringClassDescriptor; this.field = field; this.field.setAccessible(true); this.fieldType = field.getType().getSimpleName(); if (field.isAnnotationPresent(simpl_map_key_field.class)) { this.mapKeyFieldName = field.getAnnotation(simpl_map_key_field.class).value(); } // this.name = (field != null) ? field.getName() : "NULL"; derivePolymorphicDescriptors(field); this.bibtexTag = XMLTools.getBibtexTagName(field); this.isBibtexKey = XMLTools.getBibtexKey(field); this.setType(FieldType.UNSET_TYPE); // for debugging! if (annotationType == FieldType.SCALAR) { type = deriveScalarSerialization(field); } else { type = deriveNestedSerialization(field, annotationType); } String fieldName = field.getName(); StringBuilder capFieldName = new StringBuilder(fieldName); capFieldName.setCharAt(0, Character.toUpperCase(capFieldName.charAt(0))); String setMethodName = "set" + capFieldName; setValueMethod = ReflectionTools.getMethod(declaringClassDescriptor.getDescribedClass(), setMethodName, SET_METHOD_STRING_ARG); /// TODO: Nuke this deprecated method. addNamespaces(); if (javaParser != null) { comment = javaParser.getJavaDocComment(field); } Type genericType = field.getGenericType(); isGeneric = genericType instanceof ParameterizedType || genericType instanceof TypeVariable; if (genericType instanceof ParameterizedType) { // when it is parameterized, we need to take care of dependencies genericParametersString = XMLTools.getJavaGenericParametersString(field); ArrayList<Class> dependedClasses = XMLTools.getJavaGenericDependencies(field); if (dependedClasses != null) { for (Class dependedClass : dependedClasses) { addDependency(dependedClass); } } } } //TODO: THIS TYPE LOGIC IS *PURE UNADULTERATED GARBAGE*. protected FieldDescriptor(String tagName, String comment, FieldType type, ClassDescriptor elementClassDescriptor, ClassDescriptor declaringClassDescriptor, String fieldName, ScalarType scalarType, Hint xmlHint, String fieldType) { this(tagName, comment, type, elementClassDescriptor, declaringClassDescriptor, fieldName, scalarType, xmlHint, fieldType, (type == FieldType.COLLECTION_ELEMENT || type == FieldType.COLLECTION_SCALAR) ? FundamentalTypes.ARRAYLIST_TYPE : null); } protected FieldDescriptor(String tagName, String comment, FieldType type, ClassDescriptor elementClassDescriptor, ClassDescriptor declaringClassDescriptor, String fieldName, ScalarType scalarType, Hint xmlHint, String fieldType, CollectionType collectionType) { super(tagName, fieldName, comment); assert (type != FieldType.COMPOSITE_ELEMENT || elementClassDescriptor != null); this.type = type; this.elementClassDescriptor = elementClassDescriptor; this.declaringClassDescriptor = declaringClassDescriptor; this.scalarType = scalarType; this.xmlHint = xmlHint; this.fieldType = fieldType; this.collectionType = collectionType; } public String getUnresolvedScopeAnnotation() { return this.unresolvedScopeAnnotation; } protected void setUnresolvedScopeAnnotation(String scopeName) { this.unresolvedScopeAnnotation = scopeName; } public EnumerationDescriptor getEnumerationDescriptor() { return this.enumDescriptor; } /** * Process annotations for polymorphic fields. These use meta-language to map tags for translate * from based on classes (instead of field names). * * @param field * @return */ private boolean derivePolymorphicDescriptors(Field field) { // @xml_scope final simpl_scope scopeAnnotationObj = field.getAnnotation(simpl_scope.class); final String scopeAnnotation = (scopeAnnotationObj == null) ? null : scopeAnnotationObj.value(); if (scopeAnnotation != null && scopeAnnotation.length() > 0) { if (!resolveScopeAnnotation(scopeAnnotation)) { unresolvedScopeAnnotation = scopeAnnotation; declaringClassDescriptor.registerUnresolvedScopeAnnotationFD(this); } } // @xml_classes final simpl_classes classesAnnotationObj = field.getAnnotation(simpl_classes.class); final Class[] classesAnnotation = (classesAnnotationObj == null) ? null : classesAnnotationObj .value(); if ((classesAnnotation != null) && (classesAnnotation.length > 0)) { unresolvedClassesAnnotation = classesAnnotation; declaringClassDescriptor.registerUnresolvedClassesAnnotationFD(this); } return polymorphClassDescriptors != null; } /** * Register a ClassDescriptor that is polymorphically engaged with this field. * * @param classDescriptor */ protected void registerPolymorphicDescriptor(ClassDescriptor classDescriptor) { if (polymorphClassDescriptors == null) initPolymorphClassDescriptorsArrayList(1); String classTag = classDescriptor.getTagName(); polymorphClassDescriptors.put(classTag, classDescriptor); tlvClassDescriptors.put(classTag.hashCode(), classDescriptor); ArrayList<String> otherTags = classDescriptor.otherTags(); if (otherTags != null) for (String otherTag : otherTags) { if ((otherTag != null) && (otherTag.length() > 0)) { polymorphClassDescriptors.put(otherTag, classDescriptor); tlvClassDescriptors.put(otherTag.hashCode(), classDescriptor); } } } /** * Generate tag -> class mappings for a @serial_scope declaration. * * @param scopeAnnotation * Name of the scope to lookup in the global space. Must be non-null. * * @return true if the scope annotation is successfully resolved to a TranslationScope. */ private boolean resolveScopeAnnotation(final String scopeAnnotation) { println(scopeAnnotation); SimplTypesScope scope = SimplTypesScope.get(scopeAnnotation); if (scope != null) { Collection<ClassDescriptor<? extends FieldDescriptor>> scopeClassDescriptors = scope .getClassDescriptors(); initPolymorphClassDescriptorsArrayList(scopeClassDescriptors.size()); for (ClassDescriptor<? extends FieldDescriptor> classDescriptor : scopeClassDescriptors) { String tagName = classDescriptor.getTagName(); polymorphClassDescriptors.put(tagName, classDescriptor); polymorphClasses.put(tagName, classDescriptor.getDescribedClass()); tlvClassDescriptors.put(tagName.hashCode(), classDescriptor); } } else { warning("Failed to resolve simpl_scope: " + scopeAnnotation); } return scope != null; } /** * Generate tag -> class mappings for a @serial_scope declaration. * * @param scopeAnnotation * Name of the scope to lookup in the global space. Must be non-null. * * @return true if the scope annotation is successfully resolved to a TranslationScope. */ private boolean resolveClassesAnnotation(Class[] classesAnnotation) { initPolymorphClassDescriptorsArrayList(classesAnnotation.length); for (Class thatClass : classesAnnotation) { ClassDescriptor classDescriptor = ClassDescriptor.getClassDescriptor(thatClass); registerPolymorphicDescriptor(classDescriptor); polymorphClasses.put(classDescriptor.getTagName(), classDescriptor.getDescribedClass()); } return true; } /** * If there is an unresolvedScopeAnnotation, because a scope had not yet been declared when a * ClassDescriptor that uses it was constructed, try again. * * @return */ boolean resolveUnresolvedScopeAnnotation() { if (unresolvedScopeAnnotation == null) return true; boolean result = resolveScopeAnnotation(unresolvedScopeAnnotation); if (result) { unresolvedScopeAnnotation = null; } return result; } /** * If there is an unresolvedScopeAnnotation, because a scope had not yet been declared when a * ClassDescriptor that uses it was constructed, try again. * * @return */ boolean resolveUnresolvedClassesAnnotation() { if (unresolvedClassesAnnotation == null) { return true; } boolean result = resolveClassesAnnotation(unresolvedClassesAnnotation); if (result) { unresolvedClassesAnnotation = null; } return result; } /** * lazy-evaluation method. * * @return */ public ArrayList<GenericTypeVar> getGenericTypeVars() { if (genericTypeVars == null) { synchronized (this) { if (genericTypeVars == null) { deriveGenericTypeVariables(); } } } return genericTypeVars; } // added a setter to enable environment specific implementation -Fei public void setGenericTypeVars(ArrayList<GenericTypeVar> derivedGenericTypeVariables) { synchronized (this) { genericTypeVars = derivedGenericTypeVariables; } } public ArrayList<GenericTypeVar> getGenericTypeVarsContext() { return genericTypeVarsContextCD.getGenericTypeVars(); } // This method is modified, refer to FundamentalPlatformSpecific package -Fei private void deriveGenericTypeVariables() { FundamentalPlatformSpecifics.get().deriveFieldGenericTypeVars(this); } private void initPolymorphClassDescriptorsArrayList(int initialSize) { if (polymorphClassDescriptors == null) { polymorphClassDescriptors = new HashMapArrayList<String, ClassDescriptor>(initialSize); } if (polymorphClasses == null) { polymorphClasses = new HashMap<String, Class>(initialSize); } if (tlvClassDescriptors == null) { tlvClassDescriptors = new HashMap<Integer, ClassDescriptor>(initialSize); } } /** * Bind the ScalarType for a scalar typed field (attribute, leaf node, text). As appropriate, * derive other context for scalar fields (is leaf, format). * <p/> * This method should only be called when you already know the field has a scalar annotation. * * @param scalarField * Source for class & for annotations. * * @return SCALAR, IGNORED_ATTRIBUTE< or IGNORED_ELEMENT */ private FieldType deriveScalarSerialization(Field scalarField) { FieldType result = deriveScalarSerialization(scalarField.getType(), scalarField); if (xmlHint == Hint.XML_TEXT || xmlHint == Hint.XML_TEXT_CDATA) { this.declaringClassDescriptor.setScalarTextFD(this); } return result; } /** * Check for serialization hints for the field. * * Lookup the scalar type for the class, and any serialization details, such as needsEscaping & * format. * * @param thatClass * Class that we seek a ScalarType for. * @param field * Field to acquire annotations about the serialization. * * @return SCALAR, IGNORED_ATTRIBUTE< or IGNORED_ELEMENT */ private FieldType deriveScalarSerialization(Class thatClass, Field field) { isEnum = XMLTools.isEnum(field); xmlHint = XMLTools.simplHint(field); // TODO -- confirm that default case is acceptable scalarType = TypeRegistry.getScalarType(thatClass); this.fieldTypeSimpleName = field.getType().getSimpleName(); if(this.isEnum) { try{ this.enumDescriptor = EnumerationDescriptor.get(thatClass); } catch(SIMPLDescriptionException sde) { throw new RuntimeException(sde); } } if (isEnum == false && scalarType == null) { String msg = "Can't find ScalarType to serialize field: \t\t" + thatClass.getSimpleName() + "\t" + field.getName() + ";"; warning("In class " + declaringClassDescriptor.getDescribedClass().getName(), msg); return (xmlHint == Hint.XML_ATTRIBUTE) ? FieldType.IGNORED_ATTRIBUTE : FieldType.IGNORED_ELEMENT; } format = XMLTools.getFormatAnnotation(field); if (xmlHint != Hint.XML_ATTRIBUTE) { needsEscaping = scalarType.needsEscaping(); isCDATA = xmlHint == Hint.XML_LEAF_CDATA || xmlHint == Hint.XML_TEXT_CDATA; } simpl_filter filterAnnotation = field.getAnnotation(simpl_filter.class); if (filterAnnotation != null) { String regexString = filterAnnotation.regex(); if (regexString != null && regexString.length() > 0) { filterRegex = Pattern.compile(regexString); filterGroup = filterAnnotation.group(); filterReplace = filterAnnotation.replace(); } } return FieldType.SCALAR; } /** * Figure out the type of field. Build associated data structures, such as collection or element * class & tag. Process @simpl_other_tags. * * @param field * @param annotationType * Partial type information from the field declaration annotations, which are required. */ private FieldType deriveNestedSerialization(Field field, FieldType annotationType) { FieldType result = annotationType; Class fieldClass = field.getType(); switch (annotationType) { case COMPOSITE_ELEMENT: String compositeTag = field.getAnnotation(simpl_composite.class).value(); Boolean isWrap = field.isAnnotationPresent(simpl_wrap.class); boolean compositeTagIsNullOrEmpty = StringTools.isNullOrEmpty(compositeTag); if (!isPolymorphic()) { if (isWrap && compositeTagIsNullOrEmpty) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tCan't translate @simpl_composite() " + field.getName() + " because its tag argument is missing."); return FieldType.IGNORED_ELEMENT; } if (!isWrap & !compositeTagIsNullOrEmpty) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tIgnoring argument to @simpl_composite() " + field.getName() + " because it is declared polymorphic."); } elementClassDescriptor = ClassDescriptor.getClassDescriptor(fieldClass); elementClass = elementClassDescriptor.getDescribedClass(); compositeTag = XMLTools.getXmlTagName(field); } else { if (!compositeTagIsNullOrEmpty) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tIgnoring argument to @simpl_composite() " + field.getName() + " because it is declared polymorphic."); } } compositeTagName = compositeTag; break; case COLLECTION_ELEMENT: final String collectionTag = field.getAnnotation(simpl_collection.class).value(); if (!checkAssignableFrom(Collection.class, field, fieldClass, "@xml_collection")) { return FieldType.IGNORED_ELEMENT; } if (!isPolymorphic()) { Class collectionElementClass = getTypeArgClass(field, 0); // 0th type arg for // Collection<FooState> if (collectionTag == null || collectionTag.isEmpty()) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tCan't translate @xml_collection() " + field.getName() + " because its tag argument is missing."); return FieldType.IGNORED_ELEMENT; } if (collectionElementClass == null) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tCan't translate @xml_collection() " + field.getName() + " because the parameterized type argument for the Collection is missing."); return FieldType.IGNORED_ELEMENT; } if (!TypeRegistry.containsScalarType(collectionElementClass)) { elementClassDescriptor = ClassDescriptor.getClassDescriptor(collectionElementClass); elementClass = elementClassDescriptor.getDescribedClass(); } else { result = FieldType.COLLECTION_SCALAR; deriveScalarSerialization(collectionElementClass, field); // FIXME -- add error handling for IGNORED due to scalar type lookup fails if (scalarType == null) { result = FieldType.IGNORED_ELEMENT; warning("Can't identify ScalarType for serialization of " + collectionElementClass); } } } else { // If Polymorphic... if (collectionTag != null && !collectionTag.isEmpty()) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tIgnoring argument to @xml_collection() " + field.getName() + " because it is declared polymorphic with @xml_classes."); } } collectionOrMapTagName = collectionTag; collectionType = TypeRegistry.getCollectionType(field); break; case MAP_ELEMENT: String mapTag = field.getAnnotation(simpl_map.class).value(); if (!checkAssignableFrom(Map.class, field, fieldClass, "@xml_map")) { return FieldType.IGNORED_ELEMENT; } if (!isPolymorphic()) { Class mapElementClass = getTypeArgClass(field, 1); // "1st" type arg for Map<FooState> if (mapTag == null || mapTag.isEmpty()) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tCan't translate @xml_map() " + field.getName() + " because its tag argument is missing."); return FieldType.IGNORED_ELEMENT; } if (mapElementClass == null) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tCan't translate @xml_map() " + field.getName() + " because the parameterized type argument for the Collection is missing."); return FieldType.IGNORED_ELEMENT; } elementClassDescriptor = ClassDescriptor.getClassDescriptor(mapElementClass); elementClass = elementClassDescriptor.getDescribedClass(); // } // else // { // result = MAP_SCALAR; // TODO -- do we really support this case?? // // FIXME -- add error handling for IGNORED due to scalar type lookup fails // deriveScalarSerialization(mapElementClass, field); // } } else { if (mapTag != null && !mapTag.isEmpty()) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tIgnoring argument to @xml_map() " + field.getName() + " because it is declared polymorphic with @xml_classes."); } } collectionOrMapTagName = mapTag; collectionType = TypeRegistry.getCollectionType(field); break; default: break; } switch (annotationType) // set-up wrap as appropriate { case COLLECTION_ELEMENT: case MAP_ELEMENT: if (!field.isAnnotationPresent(simpl_nowrap.class)) { wrapped = true; } collectionType = TypeRegistry.getCollectionType(field); break; case COMPOSITE_ELEMENT: if (field.isAnnotationPresent(simpl_wrap.class)) { wrapped = true; } break; } if (result == FieldType.UNSET_TYPE) { warning("Programmer error -- can't derive type."); result = FieldType.IGNORED_ELEMENT; } return result; } public CollectionType getCollectionType() { return collectionType; } private boolean checkAssignableFrom(Class targetClass, Field field, Class fieldClass, String annotationDescription) { boolean result = targetClass.isAssignableFrom(fieldClass); if (!result) { warning("In " + declaringClassDescriptor.getDescribedClass() + "\n\tCan't translate " + annotationDescription + "() " + field.getName() + " because the annotated field is not an instance of " + targetClass.getSimpleName() + "."); } return result; } /** * Get the value of the ith declared type argument from a field declaration. Only works when the * type variable is directly instantiated in the declaration. * <p/> * DOES NOT WORK when the type variable is instantiated outside the declaration, and passed in. * This is because in Java, generic type variables are (lamely!) erased after compile time. They * do not exist at runtime :-( :-( :-( * * @param field * @param i * Index of the type variable in the field declaration. * * @return The class of the type variable, if it exists. */ // This method is modified to enable platform specific implementation public Class<?> getTypeArgClass(Field field, int i) { return FundamentalPlatformSpecifics.get().getTypeArgClass(field, i, this); } /** * * @return true if this field represents a ScalarType, not a nested element or collection thereof. */ public boolean isScalar() { return scalarType != null; } public boolean isCollection() { FieldType ft = type; switch (ft) { case MAP_ELEMENT: case MAP_SCALAR: case COLLECTION_ELEMENT: case COLLECTION_SCALAR: return true; default: return false; } } public boolean isNested() { return this.getType() == FieldType.COMPOSITE_ELEMENT; } public boolean isEnum() { return isEnum; } public Hint getXmlHint() { return xmlHint; } public boolean set(Object context, String valueString) { return set(context, valueString, null); } /** * In the supplied context object, set the *typed* value of the field, using the valueString * passed in. Unmarshalling is performed automatically, by the ScalarType already stored in this. * <p/> * Use a set method, if one is defined. * * @param context * ElementState object to set the Field in this. * * @param valueString * The value to set, which this method will use with the ScalarType, to create the value * that will be set. */ public boolean set(Object context, String valueString, ScalarUnmarshallingContext scalarUnMarshallingContext) { boolean result = false; if (context != null && isScalar()) { result = scalarType.setField(context, field, valueString, null, scalarUnMarshallingContext); } return result; } /** * In the supplied context object, set the non-scalar field to a non-scalar value. * * @param context * * @param value * An ElementState, or a Collection, or a Map. */ public void set(Object context, Object value) { if (!isScalar()) { setField(context, value); } } public void setField(Object context, Object value) { try { field.set(context, value); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { try { field.setAccessible(true); field.set(context, value); } catch(Exception ee) { ee.printStackTrace(); } } } public void setFieldToScalarDefault(Object context, ScalarUnmarshallingContext scalarContext) { if(this.isScalar() && this.isEnum() == false) { if(this.scalarType == null) { throw new RuntimeException("isScalar() evaluated to true, but scalarType is null!"); } this.setFieldToScalar(context, this.scalarType.defaultValueString(), scalarContext); }else{ warning("setFieldToScalarDefault called on non-scalar field"); } } /** * Get the String representation of the value of the field, in the context object, using the * ScalarType. * * @param context * @return */ public String getValueString(Object context) { String result = NULL; if (context != null && isScalar()) { result = scalarType.toString(field, context); } return result; } /** * NB: For polymorphic fields, the value of this field is meaningless, except for wrapped * collections and maps. * * @return The tag name that this field is translated to XML with. */ public String getBibtexTagName() { if (bibtexTag == null || bibtexTag.equals("")) bibtexTag = tagName; return bibtexTag; } /** * @return the scalarType of the field */ public ScalarType<?> getScalarType() { return scalarType; } /** * @return the field */ public Field getField() { return field; } /** * @return the class of the field */ public Class<?> getFieldType() { return field.getType(); } /** * * @return The OptimizationTypes type of the field. */ public FieldType getType() { return type; } public void setType(FieldType ft) { this.type = ft; } public Object getNested(Object context) { return ReflectionTools.getFieldValue(context, field); } public Map getMap(Object context) { return (Map) ReflectionTools.getFieldValue(context, field); } public Collection getCollection(Object context) { return (Collection) ReflectionTools.getFieldValue(context, field); } public boolean isMixin() { return false; } public Object getAndPerhapsCreateNested(Object context) { Object result = getNested(context); if (result == null) { result = ReflectionTools.getInstance(field.getType()); ReflectionTools.setFieldValue(context, field, result); } return result; } public boolean isWrapped() { return wrapped; } protected void setWrapped(boolean wrapped) { this.wrapped = wrapped; } public boolean isDefaultValue(String value) { return scalarType.isDefaultValue(value); } public boolean isDefaultValueFromContext(Object context) throws SIMPLTranslationException { try { if (context != null) { return scalarType.isDefaultValue(this.field, context); } return false; } catch (Exception ex) { throw new SIMPLTranslationException("checking for defalut value raised exception ", ex); } } public boolean isDefaultValue(Object value) { if (this.getType() == FieldType.SCALAR) { return value == null || isDefaultValue(value.toString()); } else { return value == null; } } /** * Appends the label and value of a metadata field to HTML elements, including anchors where * appropriate * * @param context * @param serializationContext * @param tr * @param labelString * @param labelCssClass * @param valueCssClass * @param navigatesFD * @param schemaOrgItemProp * * @throws IllegalArgumentException * @throws IllegalAccessException * @throws IOException */ public void appendHtmlValueAsAttribute(Object context, TranslationContext serializationContext, Tr tr, String labelString, String labelCssClass, String valueCssClass, FieldDescriptor navigatesFD, String schemaOrgItemProp) throws IllegalArgumentException, IllegalAccessException, IOException { if (!scalarType.isDefaultValue(field, context)) { Td labelTd = new Td(); Td valueTd = new Td(); Div valueDiv = new Div(); Div labelDiv = new Div(); A labelAnchor = new A(); A valueAnchor = new A(); if (valueCssClass != null) // does this cause problems? if so, is it because mmd is wrong? { // andruid & aaron 7/8/11 valueDiv.setCssClass(valueCssClass); } if (schemaOrgItemProp != null) { valueDiv.setSchemaOrgItemProp(schemaOrgItemProp); } labelTd.setAlign("right"); labelTd.setCssClass(labelCssClass); ScalarType navigatesScalarType = null; if (navigatesFD != null) { StringBuilder navigatesToBuffy = new StringBuilder(); navigatesScalarType = navigatesFD.getScalarType(); navigatesScalarType.appendValue(navigatesToBuffy, navigatesFD, context, serializationContext, Format.XML); labelAnchor.setHref(navigatesToBuffy.toString()); labelAnchor.setLink(labelString); labelDiv.members.add(labelAnchor); } else { labelDiv.setText(labelString); } labelTd.items.add(labelDiv); tr.cells.add(labelTd); StringBuilder valueBuffy = new StringBuilder(); scalarType.appendValue(valueBuffy, this, context, serializationContext, Format.XML); if (navigatesFD != null) { StringBuilder buffy = new StringBuilder(); navigatesScalarType.appendValue(buffy, navigatesFD, context, serializationContext, Format.XML); valueAnchor.setHref(buffy.toString()); valueAnchor.setLink(valueBuffy.toString()); valueDiv.members.add(valueAnchor); } else { valueDiv.setText(valueBuffy.toString()); } valueTd.items.add(valueDiv); tr.cells.add(valueTd); } } public String getHtmlCompositeCollectionValue(Object instance, boolean isFirst) throws IllegalArgumentException, IllegalAccessException, IOException { StringBuilder value = new StringBuilder(); if (instance != null) { if (!isFirst) { value.append(", "); } scalarType.appendValue(value, this, instance, null, Format.XML); } return value.toString(); } void appendJSONCollectionAttribute(Appendable appendable, Object instance, boolean isFirst) throws IllegalArgumentException, IllegalAccessException, IOException { if (instance != null) { if (!isFirst) { appendable.append(','); } ScalarType scalarType = this.scalarType; appendable.append('"'); scalarType.appendValue(instance, appendable, false, null, Format.JSON); appendable.append('"'); } } public boolean isCDATA() { return isCDATA; } public boolean isNeedsEscaping() { return needsEscaping; } public String[] getFormat() { return format; } @Override public String toString() { String name = (field != null) ? field.getName() : "NO_FIELD"; String clazz = declaringClassDescriptor == null ? "NO_CLASS" : declaringClassDescriptor.getDescribedClass().toString(); String typeStr = type == null ? "NO_TYPE" : Integer.toHexString(type.getTypeID()); return this.getClassSimpleName() + "[" + name + " < " + clazz + " type=0x" + typeStr + "]"; } /** * If this field is polymorphic, a Collection of ClassDescriptors for the polymorphically * associated classes. * * @return Collection, or null, if the field is not polymorphic */ public Collection<ClassDescriptor> getPolymorphicClassDescriptors() { return (polymorphClassDescriptors == null || polymorphClassDescriptors.size() == 0) ? null : polymorphClassDescriptors.values(); } /** * If this field is polymorphic, a Collection of Strings of all possible tags for the * polymorphically associated classes. This is usually the tagName field of each ClassDescriptor. * But it may be more, specifically if any of the classes are defined with @simpl_other_tags. * * @return Collection, or null, if the field is not polymorphic */ public Collection<String> getPolymorphicTags() { return (polymorphClassDescriptors == null || polymorphClassDescriptors.size() == 0) ? null : polymorphClassDescriptors.keySet(); } public HashMap<String, Class> getPolymorphicClasses() { if (polymorphClasses == null) { return null; } else { return polymorphClasses; } } public void writeHtmlWrap(boolean close, int size, String displayLabel, Tr tr) throws IOException { Input button = new Input(); button.setType("image"); button.setCssClass("general"); button.setSrc("http://ecologylab.net/cf/compositionIncludes/button.jpg"); button.setValue(""); // Td td = new Td(); Td fieldName = new Td(); Div text = new Div(); text.setCssClass("metadata_text"); fieldName.setCssClass("metadata_field_name"); // td.setCssClass("nested_field_value"); // if (size > 1) text.members.add(button); String s = displayLabel; if (size > 1) { s += " (" + Integer.toString(size) + ")"; } text.setText(s); fieldName.items.add(text); tr.cells.add(fieldName); } public void writeCompositeHtmlWrap(boolean close, String displayLabel, String schemaItemType, Tr tr) throws IOException { // Td td = new Td(); Td fieldName = new Td(); Div text = new Div(); if (schemaItemType != null) { text.setSchemaOrgItemType(schemaItemType); } text.setCssClass("metadata_text"); fieldName.setCssClass("metadata_field_name"); // td.setCssClass("nested_field_value"); text.setText(displayLabel); fieldName.items.add(text); tr.cells.add(fieldName); } // ----------------------------- methods from TagDescriptor // ---------------------------------------// /** * Use a set method or the type system to set our field in the context to the value. * * @param context * @param value * @param scalarUnmarshallingContext * TODO */ public void setFieldToScalar(Object context, String value, ScalarUnmarshallingContext scalarUnmarshallingContext) { // Allows for empty values. if (value == null) { return; } value = filterValue(value); if (!isCDATA) { value = XMLTools.unescapeXML(value); } if(this.isEnum) { try{ Object unmarshalledEnum = this.getEnumerationDescriptor().unmarshal(value); this.setField(context, unmarshalledEnum); } catch(Exception e) { // We need better error handling here, obvi. but this should bubble up an exception // to current error handling code. throw new RuntimeException(e); } } else { if (setValueMethod != null) { // if the method is found, invoke the method // fill the String value with the value of the attr node // args is the array of objects containing arguments to the method to be invoked // in our case, methods have only one arg: the value String Object[] args = new Object[1]; args[0] = value; try { setValueMethod.invoke(context, args); // run set method! } catch (InvocationTargetException e) { weird("couldnt run set method for " + tagName + " even though we found it"); e.printStackTrace(); } catch (IllegalAccessException e) { weird("couldnt run set method for " + tagName + " even though we found it"); e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } } else if (scalarType != null && !scalarType.isMarshallOnly()) { scalarType.setField(context, field, value, format, scalarUnmarshallingContext); } } } public void setRegexFilter(Pattern regex, int group, String replacement) { filterRegex = regex; filterGroup = group; filterReplace = replacement; } /** * Filter value using filterRegex. * * @param value * @return */ String filterValue(String value) { if (filterRegex != null) { Matcher matcher = filterRegex.matcher(value); if (filterReplace == null) { if (matcher.find()) { value = matcher.group(filterGroup); } else { value = ""; } } else { value = matcher.replaceAll(filterReplace); } } return value; } /** * Assume the first child of the leaf node is a text node. Pull the text of out that text node. * Trim it, and if necessary, unescape it. * * @param leafNode * The leaf node with the text element value. * @return Null if there's not really any text, or the useful text from the Node, if there is * some. */ String getLeafNodeValue(Node leafNode) { String result = null; Node textElementChild = leafNode.getFirstChild(); if (textElementChild != null) { if (textElementChild != null) { String textNodeValue = textElementChild.getNodeValue(); if (textNodeValue != null) { textNodeValue = textNodeValue.trim(); if (!isCDATA && (scalarType != null) && scalarType.needsEscaping()) textNodeValue = XMLTools.unescapeXML(textNodeValue); // debug("setting special text node " +childFieldName +"="+textNodeValue); if (textNodeValue.length() > 0) { result = textNodeValue; } } } } return result; } /** * Add element derived from the Node to a Collection. * * @param activeES * Contextualizing object that has the Collection slot we're adding to. * @param scalarUnmarshallingContext * TODO * @param childLeafNode * XML leafNode that has the value we need to add, after type conversion. * @throws SIMPLTranslationException */ public void addLeafNodeToCollection(Object activeES, String leafNodeValue, ScalarUnmarshallingContext scalarUnmarshallingContext) throws SIMPLTranslationException { if (leafNodeValue != null) { // silently ignore null leaf node values } if (scalarType != null) { // TODO -- for performance reasons, should we call without format if format is null, and // let the ScalarTypes that don't use format implement the 1 argument signature?! Object typeConvertedValue = scalarType.getInstance(leafNodeValue, format, scalarUnmarshallingContext); try { // TODO -- should we be doing this check for null here?? if (typeConvertedValue != null) { // Collection collection = (Collection) field.get(activeES); // if (collection == null) // { // // well, why not create the collection object for them?! // Collection thatCollection = // ReflectionTools.getInstance((Class<Collection>) field.getType()); // // } Collection<Object> collection = (Collection<Object>) automaticLazyGetCollectionOrMap(activeES); collection.add(typeConvertedValue); } } catch (Exception e) { throw fieldAccessException(typeConvertedValue, e); } } else { reportFieldTypeError(leafNodeValue); } } private void reportFieldTypeError(String textNodeValue) { error("Can't set to " + textNodeValue + " because fieldType is unknown."); } /** * Generate an exception about problems accessing a field. * * @param nestedElementState * @param e * @return */ private SIMPLTranslationException fieldAccessException(Object nestedElementState, Exception e) { return new SIMPLTranslationException("Unexpected Object / Field set problem. \n\t" + "Field = " + field + "\n\ttrying to set to " + nestedElementState.getClass(), e); } /** * Use the Field of this to seek a Collection or Map object in the activeES. If non-null, great -- * return it. * <p/> * Otherwise, lazy evaluation. Since the value of the field is null, use the Type of the Field to * instantiate a newInstance. Set the instance of the Field in activeES to this newInstance, and * return it. * * @param activeES * @return */ public Object automaticLazyGetCollectionOrMap(Object activeES) { Object collection = null; try { collection = field.get(activeES); if (collection == null) { collection = collectionType.getInstance(); field.set(activeES, collection); } } catch (IllegalArgumentException e) { weird("Trying to addElementToCollection(). Can't access collection field " + field.getType() + " in " + activeES); e.printStackTrace(); // return; } catch (IllegalAccessException e) { weird("Trying to addElementToCollection(). Can't access collection field " + field.getType() + " in " + activeES); e.printStackTrace(); // return; } return collection; } public ClassDescriptor getChildClassDescriptor(String tagName) { resolveUnresolvedClassesAnnotation(); resolveUnresolvedScopeAnnotation(); ClassDescriptor childClassDescriptor = !isPolymorphic() ? elementClassDescriptor : polymorphClassDescriptors.get(tagName); return childClassDescriptor; } public ClassDescriptor getChildClassDescriptor(int tlvId) { ClassDescriptor childClassDescriptor = !isPolymorphic() ? elementClassDescriptor : tlvClassDescriptors.get(tlvId); return childClassDescriptor; } Object constructChildElementState(ElementState parent, String tagName) throws SIMPLTranslationException { ClassDescriptor childClassDescriptor = !isPolymorphic() ? elementClassDescriptor : polymorphClassDescriptors.get(tagName); Object result = null; if (childClassDescriptor != null) { result = getInstance(childClassDescriptor); // if (result != null) // result.setupInParent(parent, childClassDescriptor); } return result; } private Object getInstance(ClassDescriptor childClassDescriptor) throws SIMPLTranslationException { return childClassDescriptor.getInstance(); } public void setFieldToComposite(Object context, Object nestedObject) throws SIMPLTranslationException { try { field.set(context, nestedObject); } catch (Exception e) { throw fieldAccessException(nestedObject, e); } } // ----------------------------- constant instances ---------------------------------------// public static FieldDescriptor makeIgnoredFieldDescriptor(String tag) { return new FieldDescriptor(tag); } FieldDescriptor(String tag) { this.tagName = tag; this.type = FieldType.IGNORED_ELEMENT; this.field = null; this.declaringClassDescriptor = null; } public static final FieldDescriptor IGNORED_ELEMENT_FIELD_DESCRIPTOR; static { IGNORED_ELEMENT_FIELD_DESCRIPTOR = new FieldDescriptor("IGNORED"); } // ----------------------------- convenience methods ---------------------------------------// public String elementName(int tlvId) { return isPolymorphic() ? elementClassDescriptor(tlvId).pseudoFieldDescriptor().getTagName() : isCollection() ? collectionOrMapTagName : tagName; } public String elementStart() { return isCollection() ? collectionOrMapTagName : isNested() ? compositeTagName : tagName; } /** * Most fields derive their tag from Field name for marshaling. However, some, such as those * annotated with @xml_class, @xml_classes, @xml_scope, derive their tag from the class of an * instance. This includes all polymorphic fields. * * @return true if the tag name name is derived from the class name ( not the usual case, but * needed for polymorphism). * * else if the tag name is derived from the class name for @xml_nested or, for @xml_collection * and @xml_map), the tag name is derived from the annotation's value */ public boolean isPolymorphic() { return (polymorphClassDescriptors != null) || (unresolvedScopeAnnotation != null) || (unresolvedClassesAnnotation != null); // else return true; // return tagClassDescriptors != null; } public String getCollectionOrMapTagName() { return collectionOrMapTagName; } protected void setCollectionOrMapTagName(String collectionOrMapTagName) { this.collectionOrMapTagName = collectionOrMapTagName; } // FIXME -- these are temporary bullshit declarations which need to be turned into something real public boolean hasXmlText() { return false; } public boolean isXmlNsDecl() { return false; } /** * Used to describe scalar types used for serializing the type system, itself. They cannot be * unmarshalled in Java, only marshalled. Code may be written to access their String * representations in other languages. * * @return false for almost all ScalarTypes and for all element fields */ public boolean isMarshallOnly() { return scalarType != null && scalarType.isMarshallOnly(); } public FieldDescriptor getWrappedFD() { return wrappedFD; } protected void setWrappedFD(FieldDescriptor wrappedFD) { this.wrappedFD = wrappedFD; wrappedFD.wrapper = this; } public boolean belongsTo(ClassDescriptor c) { // FIXME here should we use ClassDescriptor instead of Class? this is used by java code gen. return (this.getDeclaringClassDescriptor() == c); // return this.getDeclaringClassDescriptor().getDescribedClass() == c.getDescribedClass(); } @Override public ArrayList<String> otherTags() { ArrayList<String> result = this.otherTags; if (result == null) { result = new ArrayList<String>(); if (this.getField() != null) { final simpl_other_tags otherTagsAnnotation = this.getField().getAnnotation( simpl_other_tags.class); // commented out since getAnnotation also includes inherited annotations // ElementState.xml_other_tags otherTagsAnnotation = // thisClass.getAnnotation(ElementState.xml_other_tags.class); if (otherTagsAnnotation != null) for (String otherTag : otherTagsAnnotation.value()) result.add(otherTag); } this.otherTags = result; } return result; } public String getObjectiveCType() { if (collectionType != null) { return collectionType.deriveObjectiveCTypeName(); } else if (scalarType != null) { return scalarType.deriveObjectiveCTypeName(); } return null; } public String getCSharpType() { String result = null; if (collectionType != null) { result = collectionType.deriveCSharpTypeName(); } else if (scalarType != null /* && !isCollection() */) { result = scalarType.deriveCSharpTypeName(); int lastDot = result.lastIndexOf('.'); if (lastDot > 0 && lastDot < result.length() - 1) result = result.substring(lastDot + 1); } else { String name = fieldType; if (name != null && !name.contains("$")) // FIXME:Dealing with inner classes is not done yet result = name; // } } if (this.IsGeneric()) { // FIXME does not handles scalar type name translation in generic parameters! result += getGenericParametersString(); } return result; } public String getJavaType() { StringBuilder sb = StringBuilderBaseUtils.acquire(); if (collectionType != null) { sb.append(collectionType.getJavaTypeName()); } if (scalarType != null && !isCollection()) { sb.append(scalarType.getSimpleName()); } else { String name = fieldType; if (name != null && !name.contains("$")) // FIXME:Dealing with inner classes is not done yet sb.replace(0, sb.length(), name); } if (this.IsGeneric() && sb.indexOf("<") < 0) { List<GenericTypeVar> genericTypeVars = getGenericTypeVars(); if (genericTypeVars != null && genericTypeVars.size() > 0) { sb.append('<'); for (int i = 0; i < genericTypeVars.size(); ++i) { sb.append(i == 0 ? "" : ", ").append(genericTypeVars.get(i).getName()); } sb.append('>'); } } String result = sb.toString(); StringBuilderBaseUtils.release(sb); return result; } public int getTLVId() { return elementStart().hashCode(); } public void writeTLVWrap(DataOutputStream outputBuffer, ByteArrayOutputStream collectionBuffy) throws IOException { outputBuffer.writeInt(getWrappedTLVId()); outputBuffer.writeInt(collectionBuffy.size()); collectionBuffy.writeTo(outputBuffer); } public int getWrappedTLVId() { int tempTLVId = 0; if (tagName != null) tempTLVId = tagName.hashCode(); return tempTLVId; } public void writeJSONCollectionClose(PrintStream appendable) { appendable.append(']'); } public ClassDescriptor getDeclaringClassDescriptor() { return declaringClassDescriptor; } public ClassDescriptor getElementClassDescriptor() { return elementClassDescriptor; } /** * @return the name of the field used for key in this map. this is indicated through {@code * @simpl_map_key_field}, and is de/serializable. */ public String getMapKeyFieldName() { return this.mapKeyFieldName; } public Object getMapKeyFieldValue(Object mapElement) { if (this.mapKeyFieldName != null) { ClassDescriptor cd = ClassDescriptor.getClassDescriptor(mapElement); if (cd != null) { FieldDescriptor fd = cd.getFieldDescriptorByFieldName(mapKeyFieldName); return fd.getValue(mapElement); } } return null; } public void setElementClassDescriptor(ClassDescriptor elementClassDescriptor) { this.elementClassDescriptor = elementClassDescriptor; Class elementClass = elementClassDescriptor.getDescribedClass(); if (elementClass != null) this.elementClass = elementClass; } public ClassDescriptor elementClassDescriptor(String tagName) { if (isPolymorphic()) { if (polymorphClassDescriptors == null) derivePolymorphicDescriptors(field); if (polymorphClassDescriptors != null) return polymorphClassDescriptors.get(tagName); } return elementClassDescriptor; } public ClassDescriptor elementClassDescriptor(int tlvId) { return (!isPolymorphic()) ? elementClassDescriptor : tlvClassDescriptors.get(tlvId); } public void writeBibtexCollectionStart(PrintStream appendable) { appendable.append('\n'); appendable.append(' '); appendable.append(tagName); appendable.append('='); } /** * A method to add the namespaces corresponds to the field descriptor. */ @Deprecated private void addNamespaces() { ArrayList<Class<?>> genericClasses = XMLTools.getGenericParameters(field); Class typeClass = field.getType(); if (genericClasses != null) for (Class genericClass : genericClasses) { if (ElementState.class.isAssignableFrom(genericClass)) { libraryNamespaces.put(genericClass.getPackage().getName(), genericClass.getPackage() .getName()); } } if (typeClass != null) { if (ElementState.class.isAssignableFrom(typeClass)) { libraryNamespaces.put(typeClass.getPackage().getName(), typeClass.getPackage().getName()); } } } /** * method to access the namespace information related to field descriptor * * @return HashMap <String, String> */ @Deprecated public HashMap<String, String> getNamespaces() { return libraryNamespaces; } @Override public String key() { return this.name; } public boolean IsGeneric() { return isGeneric; } public String getGenericParametersString() { return genericParametersString; } public ArrayList<ClassDescriptor> getDependencies() { return dependencies; } public void addDependency(ClassDescriptor dependedClassD) { if (dependencies == null) dependencies = new ArrayList<ClassDescriptor>(); dependencies.add(dependedClassD); } public void addDependency(Class dependedClass) { // for those classes not SIMPL-enabled, this creates a surrogate class descriptor. addDependency(new ClassDescriptor(dependedClass)); } /** * @return The Java name of the ElementState subclass or ScalarType of the this, depending on * whether it is composite or scalar. */ @Override public String getJavaTypeName() { return elementClassDescriptor != null ? elementClassDescriptor.getJavaTypeName() : scalarType .getJavaTypeName(); } @Override public String getCSharpTypeName() { String cSharpTypeName = elementClassDescriptor == null ? null : elementClassDescriptor .getCSharpTypeName(); if (cSharpTypeName == null) return scalarType.getCSharpTypeName(); else { // FIXME SIMPL does not handle map of scalar correctly! ScalarType possibleMapScalarType = TypeRegistry.getScalarTypeByName(cSharpTypeName); if (possibleMapScalarType != null) return possibleMapScalarType.getCSharpTypeName(); else return cSharpTypeName; } } @Override public String getCSharpNamespace() { if (this.isScalar()) return this.getScalarType().getCSharpNamespace(); else if (this.isCollection()) return this.getCollectionType().getCSharpNamespace(); else return this.getElementClassDescriptor().getCSharpNamespace(); } @Override public String getObjectiveCTypeName() { return elementClassDescriptor != null ? elementClassDescriptor.getObjectiveCTypeName() : isPolymorphic() || isEnum ? this.fieldType : scalarType.getObjectiveCTypeName(); } @Override public String getDbTypeName() { return elementClassDescriptor != null ? elementClassDescriptor.getDbTypeName() : scalarType .getDbTypeName(); } public Object getValue(Object context) { Object resultObject = null; Field childField = this.getField(); try { resultObject = childField.get(context); } catch (IllegalAccessException e) { debugA("WARNING re-trying access! " + e.getStackTrace()[0]); childField.setAccessible(true); try { resultObject = childField.get(this); } catch (IllegalAccessException e1) { error("Can't access " + childField.getName()); e1.printStackTrace(); } } return resultObject; } public void appendValue(Appendable appendable, Object object, TranslationContext translationContext, Format format) throws SIMPLTranslationException { try { if(this.isEnum) { EnumerationDescriptor ed = this.getEnumerationDescriptor(); if(ed == null) { throw new RuntimeException("Null enumerator descriptor; add rehydration code."); } String enumValue = ed.marshal(this.getValue(object)); String escapedValue = FormatRegistry.get(format).escape(enumValue); appendable.append(escapedValue); } else { scalarType.appendValue(appendable, this, object, translationContext, format); } } catch (Exception ex) { throw new SIMPLTranslationException("appendValue exception. ", ex); } } public void appendCollectionScalarValue(Appendable appendable, Object object, TranslationContext translationContext, Format format) throws SIMPLTranslationException { try { ScalarType scalarType = this.scalarType; scalarType.appendValue(object, appendable, !isCDATA, translationContext, format); } catch (Exception ex) { throw new SIMPLTranslationException("appendValue exception. ", ex); } } public boolean isBibtexKey() { return isBibtexKey; } public boolean isCollectionTag(String tagName) { resolveUnresolvedClassesAnnotation(); resolveUnresolvedScopeAnnotation(); return isPolymorphic() ? polymorphClassDescriptors.containsKey(tagName) : collectionOrMapTagName.equals(tagName); } /** * make a SHALLOW copy of this descriptor. */ @Override public FieldDescriptor clone() { FieldDescriptor cloned = null; try { cloned = (FieldDescriptor) super.clone(); cloned.clonedFrom = this; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return cloned; } public FieldDescriptor getDescriptorClonedFrom() { return clonedFrom; } /** * @return The list of meta-information (annotations, attributes, etc.) for this field. */ public List<MetaInformation> getMetaInformation() { if (this.metaInfo == null) { this.metaInfo = new ArrayList<MetaInformation>(); FieldType type = this.getType(); String collectionMapTagValue = getCollectionOrMapTagName(); if (type == FieldType.COMPOSITE_ELEMENT) { // @simpl_composite metaInfo.add(new MetaInformation(simpl_composite.class)); // @simpl_wrap if (isWrapped()) metaInfo.add(new MetaInformation(simpl_wrap.class)); } else if (type == FieldType.COLLECTION_ELEMENT || type == FieldType.COLLECTION_SCALAR) { addDependency(List.class); // @simpl_collection if (isPolymorphic()) metaInfo.add(new MetaInformation(simpl_collection.class)); else metaInfo.add(new MetaInformation(simpl_collection.class, false, collectionMapTagValue)); // @simpl_nowrap if (!isWrapped()) { metaInfo.add(new MetaInformation(simpl_nowrap.class)); } } else if (type == FieldType.MAP_ELEMENT) { // @simpl_map metaInfo.add(new MetaInformation(simpl_map.class, false, collectionMapTagValue)); } else { // @simpl_scalar metaInfo.add(new MetaInformation(simpl_scalar.class)); // @simpl_hints Hint hint = getXmlHint(); if (hint != null) { addDependency(Hint.class); metaInfo.add(new MetaInformation(simpl_hints.class, true, hint)); } // @simpl_filter if (filterRegex != null && filterRegex.pattern().length() > 0) { List<String> argNames = new ArrayList<String>(); List<Object> argValues = new ArrayList<Object>(); argNames.add("regex"); argValues.add(filterRegex.pattern()); if (filterGroup > 0) { argNames.add("group"); argValues.add(filterGroup); } if (filterReplace != null) { argNames.add("replace"); argValues.add(filterReplace); } metaInfo.add(new MetaInformation(simpl_filter.class, argNames.toArray(new String[] {}), argValues.toArray())); } } // @simpl_tag String autoTagName = XMLTools.getXmlTagName(getName(), null); if (tagName != null && !tagName.equals("") && !tagName.equals(autoTagName)) metaInfo.add(new MetaInformation(simpl_tag.class, false, tagName)); // @simpl_other_tags ArrayList<String> otherTags = otherTags(); if (otherTags != null && otherTags.size() > 0) metaInfo.add(new MetaInformation(simpl_other_tags.class, true, otherTags.toArray())); // @simpl_classes Collection<ClassDescriptor> polyClassDescriptors = getPolymorphicClassDescriptors(); if (polyClassDescriptors != null) { List<Argument> args = new ArrayList<Argument>(); for (ClassDescriptor polyClassD : polyClassDescriptors) { Argument a = new Argument(); a.value = polyClassD; a.typeName = polyClassD.getDescribedClassName(); a.simpleTypeName = polyClassD.getDescribedClassSimpleName(); args.add(a); addDependency(polyClassD); } MetaInformation simplClasses = new MetaInformation(simpl_classes.class, true, args); metaInfo.add(simplClasses); } // @simpl_scope String polyScope = getUnresolvedScopeAnnotation(); if (polyScope != null && polyScope.length() > 0) metaInfo.add(new MetaInformation(simpl_scope.class, false, polyScope)); } return metaInfo; } public String getFieldTypeSimpleName() { return this.fieldTypeSimpleName; } public FieldDescriptor getWrapper() { return wrapper; } protected void setWrapper(FieldDescriptor wrapper) { this.wrapper = wrapper; } protected String getFieldTypeName() { return fieldType; } }