前几天说过想造一个大轮子,今天有点时间就开始写了一点代码。和上次不同的是,原来打算使用的OGNL打算放弃了,决定尽可能的使用JDK带的API和自己写的代码完成,尽可能少的使用第三方的代码,这样更好把握一些。进度可能会非常的慢,因为我会写完整的单元测试代码,并且因为公司加班的原因也没有多少时间投入这个,不过反正是我自己用,也不急。
今天的工作成果就是OGNL的简化版本:BeanAccessExpress。
语法比较简单,就是xxx.yyy[index].zzz[key]
只支持对象属性、数组、List和Map,长度不限。数组和List使用[index]访问,Map的元素使用[key]访问,key必须是字母开头的单词。
代码如下:
package com.jiehoo.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.jiehoo.util.MethodUtil;
import com.jiehoo.util.StringUtil;

public class BeanAccessExpress {
  private static final String FIELD_EXPRESS = "([a-zA-Z_]\\w*)(?:\\[(?:(\\d+)|([a-zA-Z_]\\w*))\\])?";

  protected static final Pattern expressPattern = Pattern.compile(FIELD_EXPRESS
      + "(\\." + FIELD_EXPRESS + ")*");

  private static final Pattern fieldPattern = Pattern.compile(FIELD_EXPRESS);

  public static Object get(Object bean, String express) throws Exception {
    Object result = bean;
    List fields = parseFields(express);
    for (int i = 0; i < fields.size(); i++) {
      BeanAccessField field = (BeanAccessField) fields.get(i);
      int type = field.getType();
      switch (type) {
      case BeanAccessField.NORMAL:
        result = MethodUtil.getGetter(result, field.getField()).invoke(result,
            null);
        break;
      case BeanAccessField.MAP:
        result = MethodUtil.getGetter(result, field.getField()).invoke(result,
            null);
        result = ((Map) result).get(field.getKey());
        break;
      case BeanAccessField.LIST:
        result = MethodUtil.getGetter(result, field.getField()).invoke(result,
            null);
        if (result instanceof List) {
          result = ((List) result).get(field.getIndex());
        } else {
          result = ((Object[]) result)[field.getIndex()];
        }
        break;
      }
    }
    return result;
  }

  public static Object getSilent(Object bean, String express) {
    if (bean == null) {
      return null;
    }
    Object result = bean;
    try {
      List fields = parseFields(express);
      for (int i = 0; i < fields.size(); i++) {
        BeanAccessField field = (BeanAccessField) fields.get(i);
        int type = field.getType();
        switch (type) {
        case BeanAccessField.NORMAL:
          result = MethodUtil.getGetter(result, field.getField()).invoke(
              result, null);
          break;
        case BeanAccessField.MAP:
          result = MethodUtil.getGetter(result, field.getField()).invoke(
              result, null);
          result = ((Map) result).get(field.getKey());
          break;
        case BeanAccessField.LIST:
          result = MethodUtil.getGetter(result, field.getField()).invoke(
              result, null);
          if (result instanceof List) {
            result = ((List) result).get(field.getIndex());
          } else {
            result = ((Object[]) result)[field.getIndex()];
          }
          break;
        }
      }
    } catch (Exception e) {
      return null;
    }
    return result;
  }

  private static List parseFields(String express) {
    List result = new ArrayList();
    if (!expressPattern.matcher(express).matches()) {
      throw new ExpressError();
    }
    Matcher matcher = fieldPattern.matcher(express);
    int start = 0;
    while (matcher.find(start)) {
      String field = matcher.group(1);
      String index = matcher.group(2);
      String key = matcher.group(3);
      start = matcher.end();
      if (index == null && key == null) {
        result.add(new BeanAccessField(StringUtil.uppercaseFirst(field)));
      } else if (index != null) {
        result.add(new BeanAccessField(StringUtil.uppercaseFirst(field),
            Integer.parseInt(index)));
      } else if (key != null) {
        result.add(new BeanAccessField(StringUtil.uppercaseFirst(field), key));
      }
    }
    return result;
  }
}

package com.jiehoo.core;

public class BeanAccessField {
  public static final int NORMAL = 0;

  public static final int LIST = 1;

  public static final int MAP = 2;

  private String field;

  private int type;

  private int index;

  private String key;

  public BeanAccessField(String field) {
    this.field = field;
    type = NORMAL;
  }

  public BeanAccessField(String field, int index) {
    this.field = field;
    this.index = index;
    type = LIST;
  }

  public BeanAccessField(String field, String key) {
    this.field = field;
    this.key = key;
    type = MAP;
  }

  public String getField() {
    return field;
  }

  public int getIndex() {
    return index;
  }

  public String getKey() {
    return key;
  }

  public int getType() {
    return type;
  }

}

测试用例:
package com.jiehoo.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import junit.framework.TestCase;

public class TestBeanAccessExpress extends TestCase {
  class TestBean {
    String string;

    Object[] arrays;

    List list;

    Map map;

    public TestBean() {
      string = "testbean";
      arrays = new Object[3];
      arrays[0] = "array0";
      arrays[1] = "array1";
      arrays[2] = new Object();
      list = new ArrayList();
      list.add("list0");
      list.add("list1");
      list.add(new Object());
      map = new HashMap();
      map.put("key0", "value0");
      map.put("key1", "value1");
      map.put("key2", new Object());
    }

    public Object[] getArrays() {
      return arrays;
    }

    public List getList() {
      return list;
    }

    public Map getMap() {
      return map;
    }

    public String getString() {
      return string;
    }

  }

  public void testErrorExpress() throws Exception {
    boolean exception = false;
    try {
      BeanAccessExpress.get(new TestBean(), "123");
    } catch (ExpressError e) {
      exception = true;
    }
    if (!exception) {
      fail("Should throw ExpressError.");
    }
    try {
      BeanAccessExpress.get(new TestBean(), "xx123.xedd.");
    } catch (ExpressError e) {
      exception = true;
    }
    if (!exception) {
      fail("Should throw ExpressError.");
    }
    try {
      BeanAccessExpress.get(new TestBean(), "xx123.xedd.1");
    } catch (ExpressError e) {
      exception = true;
    }
    if (!exception) {
      fail("Should throw ExpressError.");
    }
  }

  public void testSimleExpress() throws Exception {
    assertEquals("testbean", BeanAccessExpress.get(new TestBean(), "string"));
    assertEquals(String.class, BeanAccessExpress.get(new TestBean(),
        "string.class"));
  }

  public void testArrayExpress() throws Exception {
    assertEquals("array0", BeanAccessExpress.get(new TestBean(), "arrays[0]"));
    assertEquals("array1", BeanAccessExpress.get(new TestBean(), "arrays[1]"));
    assertEquals(Object.class, BeanAccessExpress.get(new TestBean(),
        "arrays[2].class"));
  }

  public void testListExpress() throws Exception {
    assertEquals("list0", BeanAccessExpress.get(new TestBean(), "list[0]"));
    assertEquals("list1", BeanAccessExpress.get(new TestBean(), "list[1]"));
    assertEquals(Object.class, BeanAccessExpress.get(new TestBean(),
        "list[2].class"));
  }

  public void testMapExpress() throws Exception {
    assertEquals("value0", BeanAccessExpress.get(new TestBean(), "map[key0]"));
    assertEquals("value1", BeanAccessExpress.get(new TestBean(), "map[key1]"));
    assertEquals(Object.class, BeanAccessExpress.get(new TestBean(),
        "map[key2].class"));
  }
}

有时间会把代码上传到服务器。

(Visited 116 times, 1 visits today)