Package com.floreysoft.jmte

Source Code of com.floreysoft.jmte.DefaultModelAdaptor

package com.floreysoft.jmte;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;

import com.floreysoft.jmte.message.NoLogErrorHandler;
import com.floreysoft.jmte.token.InvalidToken;
import com.floreysoft.jmte.token.Token;

/**
* Default implementation of the model adapter.
* <p>
* Does the object traversal using the "." operator. Resolved value will be
* checked if it is either a {@link Processor} or a {@link Callable} in which
* case the final resolved value is computed by calling those executable
* objects.
* </p>
*
* <p>
* Inherit from this adapter if you want a slight change of this behavior and
* set your new adaptor on the engine
* {@link Engine#setModelAdaptor(ModelAdaptor)} .
* </p>
*/
public class DefaultModelAdaptor implements ModelAdaptor {

  protected Map<Class<?>, Map<String, Member>> cache = new HashMap<Class<?>, Map<String, Member>>();

  public Object getValue(Map<String, Object> model, String expression) {
    String[] split = expression.split("\\.");
    List<String> segments = Arrays.asList(split);
    ErrorHandler errorHandler = new NoLogErrorHandler();
    Token token = new InvalidToken();
    Object value = traverse(segments, model, errorHandler, token);
    return value;
  }
 
 
  @Override
  @SuppressWarnings("rawtypes")
  public Object getValue(TemplateContext context, Token token,
      List<String> segments, String expression) {
    Object value = traverse(segments, context.model, context.errorHandler, token);
    // if value implements both, we use the more specialized implementation
    if (value instanceof Processor) {
      value = ((Processor) value).eval(context);
    } else if (value instanceof Callable) {
      try {
        value = ((Callable) value).call();
      } catch (Exception e) {
      }
    }
    return value;
  }

  protected Object traverse(List<String> segments, Map<String, Object> model,
      ErrorHandler errorHandler, Token token) {
    if (segments.size() == 0) {
      return null;
    }
    String objectName = segments.get(0);
    Object value = model.get(objectName);

    value = traverse(value, segments, 1, errorHandler, token);
    return value;
  }

  protected Object traverse(Object o, List<String> attributeNames, int index,
      ErrorHandler errorHandler, Token token) {
    Object result;
    if (index >= attributeNames.size()) {
      result = o;
    } else {
      if (o == null) {
        return null;
      }
      String attributeName = attributeNames.get(index);
      Object nextStep = nextStep(o, attributeName, errorHandler, token);
      result = traverse(nextStep, attributeNames, index + 1,
          errorHandler, token);
    }
    return result;
  }

  @SuppressWarnings("rawtypes")
  protected Object nextStep(Object o, String attributeName,
      ErrorHandler errorHandler, Token token) {
    Object result;
    if (o instanceof String) {
      errorHandler.error("no-call-on-string", token, new ModelBuilder(
          "receiver", o.toString()).build());
      return o;
    } else if (o instanceof Map) {
      Map map = (Map) o;
      result = map.get(attributeName);
    } else {
      try {
        result = getPropertyValue(o, attributeName);
      } catch (Exception e) {
        errorHandler.error("property-access-error", token,
            new ModelBuilder("property", attributeName, "object",
                o, "exception", e).build());
        result = "";
      }
    }
    return result;
  }

  @SuppressWarnings("rawtypes")
  protected Object getPropertyValue(Object o, String propertyName) {
    try {
      // XXX this is so strange, can not call invoke on key and value for
      // Map.Entry, so we have to get this done like this:
      if (o instanceof Map.Entry) {
        final Map.Entry entry = (Entry) o;
        if (propertyName.equals("key")) {
          final Object result = entry.getKey();
          return result;
        } else if (propertyName.equals("value")) {
          final Object result = entry.getValue();
          return result;
        }

      }
      boolean valueSet = false;
      Object value = null;
      Member member = null;
      final Class<?> clazz = o.getClass();
      Map<String, Member> members = cache.get(clazz);
      if (members == null) {
        members = new HashMap<String, Member>();
        cache.put(clazz, members);
      } else {
        member = members.get(propertyName);
        if (member != null) {
          if (member.getClass() == Method.class)
            return ((Method) member).invoke(o);
          if (member.getClass() == Field.class)
            return ((Field) member).get(o);
        }
      }

      final String suffix = Character.toUpperCase(propertyName.charAt(0))
          + propertyName.substring(1);
      final Method[] declaredMethods = clazz.getMethods();
      for (Method method : declaredMethods) {
        if (Modifier.isPublic(method.getModifiers())
            && (method.getName().equals("get" + suffix) || method
                .getName().equals("is" + suffix))) {
          value = method.invoke(o, (Object[]) null);
          valueSet = true;
          member = method;
          break;

        }
      }
      if (!valueSet) {
        final Field field = clazz.getField(propertyName);
        if (Modifier.isPublic(field.getModifiers())) {
          value = field.get(o);
          member = field;
          valueSet = true;
        }
      }
      if (valueSet) {
        members.put(propertyName, member);
      }
      return value;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}
TOP

Related Classes of com.floreysoft.jmte.DefaultModelAdaptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.