/**
  * Sets the fields value, first by attempting to call the setter method if it exists and then
  * falling back to setting the field directly.
  *
  * @param obj the object instance to set the value on, cannot be <code>null</code>.
  * @param info the fields reflected info object, cannot be <code>null</code>.
  * @param value the value to set on the field, may be <code>null</code>.
  * @throws Siren4JException upon reflection error.
  */
 public static void setFieldValue(Object obj, ReflectedInfo info, Object value)
     throws Siren4JException {
   if (obj == null) {
     throw new IllegalArgumentException("obj cannot be null");
   }
   if (info == null) {
     throw new IllegalArgumentException("info cannot be null");
   }
   if (info.getSetter() != null) {
     Method setter = info.getSetter();
     setter.setAccessible(true);
     try {
       setter.invoke(obj, new Object[] {value});
     } catch (Exception e) {
       throw new Siren4JException(e);
     }
   } else {
     // No setter set field directly
     try {
       info.getField().set(obj, value);
     } catch (Exception e) {
       throw new Siren4JException(e);
     }
   }
 }
 /**
  * Helper method to find the field info by its effective name from the passed in list of info.
  *
  * @param infoList cannot be <code>null</code>.
  * @param name cannot be <code>null</code> or empty.
  * @return the info or <code>null</code> if not found.
  */
 public static ReflectedInfo getFieldInfoByEffectiveName(
     List<ReflectedInfo> infoList, String name) {
   if (infoList == null) {
     throw new IllegalArgumentException("infoList cannot be null.");
   }
   if (StringUtils.isBlank(name)) {
     throw new IllegalArgumentException("name cannot be null or empty.");
   }
   ReflectedInfo result = null;
   for (ReflectedInfo info : infoList) {
     if (name.equals(info.getEffectiveName())) {
       result = info;
       break;
     }
   }
   return result;
 }
 /**
  * Replaces field tokens with the actual value in the fields. The field types must be simple
  * property types
  *
  * @param str
  * @param fields info
  * @param parentMode
  * @param obj
  * @return
  * @throws Siren4JException
  */
 public static String replaceFieldTokens(
     Object obj, String str, List<ReflectedInfo> fields, boolean parentMode)
     throws Siren4JException {
   Map<String, Field> index = new HashMap<String, Field>();
   if (StringUtils.isBlank(str)) {
     return str;
   }
   if (fields != null) {
     for (ReflectedInfo info : fields) {
       Field f = info.getField();
       if (f != null) {
         index.put(f.getName(), f);
       }
     }
   }
   try {
     for (String key : ReflectionUtils.getTokenKeys(str)) {
       if ((!parentMode && !key.startsWith("parent."))
           || (parentMode && key.startsWith("parent."))) {
         String fieldname = key.startsWith("parent.") ? key.substring(7) : key;
         if (index.containsKey(fieldname)) {
           Field f = index.get(fieldname);
           if (f.getType().isEnum() || ArrayUtils.contains(propertyTypes, f.getType())) {
             String replacement = "";
             Object theObject = f.get(obj);
             if (f.getType().isEnum()) {
               replacement = theObject == null ? "" : ((Enum) theObject).name();
             } else {
               replacement = theObject == null ? "" : theObject.toString();
             }
             str = str.replaceAll("\\{" + key + "\\}", Matcher.quoteReplacement("" + replacement));
           }
         }
       }
     }
   } catch (Exception e) {
     throw new Siren4JException(e);
   }
   return str;
 }