Takumi IINO avatar Takumi IINO committed 6928152

action.util: ActionFormの内容をSessionに退避するためのユーティリティ ActionFormRestore 追加

Comments (0)

Files changed (6)

sastruts-oreo/src/main/java/jp/troter/seasar/struts/action/util/ActionFormRestore.java

+package jp.troter.seasar.struts.action.util;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import jp.troter.seasar.struts.resource.Constants;
+
+import org.seasar.framework.beans.BeanDesc;
+import org.seasar.framework.beans.PropertyDesc;
+import org.seasar.framework.beans.factory.BeanDescFactory;
+import org.seasar.framework.beans.util.Beans;
+import org.seasar.framework.util.tiger.CollectionsUtil;
+
+public class ActionFormRestore {
+
+    public static final String KEY = ActionFormRestore.class.getName() + "#store";
+
+    protected ActionFormRestoreRule restoreRule;
+
+    public ActionFormRestore() {
+        this(new ActionFormRestoreRule.Default());
+    }
+
+    public ActionFormRestore(ActionFormRestoreRule restoreRule) {
+        this.restoreRule = restoreRule;
+    }
+
+    /**
+     * フォームをセッションに保存
+     * @param actionMethod
+     * @param form
+     * @param excludeProperties
+     */
+    public void store(Method actionMethod, HttpSession session, Object form, String ... excludeProperties) {
+        store(methodToName(actionMethod), session, form, excludeProperties);
+    }
+
+    /**
+     * フォームをセッションに保存
+     * @param actionClass
+     * @param form
+     * @param excludeProperties
+     */
+    public void store(Class<?> actionClass, HttpSession session, Object form, String ... excludeProperties) {
+        store(classToName(actionClass), session, form, excludeProperties);
+    }
+
+    /**
+     * フォームをセッションに保存
+     * @param namespace
+     * @param form
+     * @param excludeProperties
+     */
+    public void store(String namespace, HttpSession session, Object form, String ... excludeProperties) {
+        if (form == null) {
+            throw new NullPointerException("form");
+        }
+        HashMap<String,  ? extends Serializable> dist = CollectionsUtil.newHashMap();
+
+        IncludeAndExclude i$e = parseRule(form);
+        List<String> includes = i$e.getIncludes();
+        List<String> excludes = CollectionsUtil.newArrayList(i$e.getExcludes());
+        excludes.addAll(Arrays.asList(excludeProperties));
+
+        // copy
+        Beans.copy(form, dist)
+            .includes(includes.toArray(new String[0]))
+            .excludes(excludes.toArray(new String[0]))
+            .execute();
+
+        // save
+        getStore(session).put(namespace, dist);
+    }
+
+    /**
+     * フォームをセッションから復元 & 破棄
+     * @param <ACTION_FORM>
+     * @param actionMethod
+     * @param form
+     * @return
+     */
+    public <ACTION_FORM> ACTION_FORM restore(Method actionMethod, HttpSession session, ACTION_FORM form) {
+        return restore(methodToName(actionMethod), session, form);
+    }
+
+    /**
+     * フォームをセッションから復元 & 破棄
+     * @param <ACTION_FORM>
+     * @param actionClass
+     * @param form
+     * @return
+     */
+    public <ACTION_FORM> ACTION_FORM restore(Class<?> actionClass, HttpSession session, ACTION_FORM form) {
+        return restore(classToName(actionClass), session, form);
+    }
+
+    /**
+     * フォームをセッションから復元 & 破棄
+     * @param <ACTION_FORM>
+     * @param namespace
+     * @param form
+     * @return
+     */
+    public <ACTION_FORM> ACTION_FORM restore(String namespace, HttpSession session, ACTION_FORM form) {
+        if (form == null) {
+            throw new NullPointerException("form");
+        }
+        if (getStore(session).containsKey(namespace)) {
+            Beans.copy(getStore(session).get(namespace), form).excludesNull().execute();
+            getStore(session).remove(namespace);
+        }
+        return form;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Map<String, Object> getStore(HttpSession session) {
+        Map<String, Object> store = (Map<String, Object>)session.getAttribute(KEY);
+        if (store == null) {
+            store = CollectionsUtil.newConcurrentHashMap();
+            session.setAttribute(KEY, store);
+        }
+        return store;
+    }
+
+    /**
+     * アノテーションからincludeとexcludeのフィールド名のリストを作成
+     * @param form
+     * @return
+     */
+    protected IncludeAndExclude parseRule(Object form) {
+        List<String> includeProperties = CollectionsUtil.newArrayList();
+        List<String> excludeProperties = CollectionsUtil.newArrayList();
+        BeanDesc beanDesc = BeanDescFactory.getBeanDesc(form.getClass());
+        int size = beanDesc.getPropertyDescSize();
+        for (int i = 0; i < size; i++) {
+            PropertyDesc propertyDesc = beanDesc.getPropertyDesc(i);
+
+            if (restoreRule.isInclude(propertyDesc)) {
+                includeProperties.add(propertyDesc.getPropertyName());
+            }
+            if (restoreRule.isExclude(propertyDesc)) {
+                excludeProperties.add(propertyDesc.getPropertyName());
+            }
+        }
+        return new IncludeAndExclude(includeProperties, excludeProperties);
+    }
+
+    protected String classToName(Class<?> clazz) {
+        return classToName(clazz.getName());
+    }
+
+    protected String classToName(String className) {
+        return className.replaceAll(Constants.ENHANCED_BY_XXX_AOP_MAKER_RE, "");
+    }
+
+    protected String methodToName(Method method) {
+        return classToName(method.getClass()) + "." + method.getName();
+    }
+
+    public static class IncludeAndExclude {
+        private List<String> includes;
+        private List<String> excludes;
+        public IncludeAndExclude(List<String> includes, List<String> excludes) {
+            this.includes = includes;
+            this.excludes = excludes;
+        }
+        public List<String> getIncludes() {
+            return includes;
+        }
+        public List<String> getExcludes() {
+            return excludes;
+        }
+    }
+}

sastruts-oreo/src/main/java/jp/troter/seasar/struts/action/util/ActionFormRestoreRule.java

+package jp.troter.seasar.struts.action.util;
+
+import java.lang.annotation.Annotation;
+
+import jp.troter.seasar.struts.annotation.RestoreExclude;
+import jp.troter.seasar.struts.annotation.RestoreInclude;
+
+import org.seasar.framework.beans.PropertyDesc;
+
+/**
+ * 復元ルールを規定します
+ */
+public interface ActionFormRestoreRule {
+    /**
+     * 復元するプロパティである
+     * @param propertyDesc
+     * @return
+     */
+    boolean isInclude(PropertyDesc propertyDesc);
+
+    /**
+     * 復元しないプロパティである
+     * @param propertyDesc
+     * @return
+     */
+    boolean isExclude(PropertyDesc propertyDesc);
+
+    /**
+     * デフォルトの復元ルール
+     */
+    public static class Default implements ActionFormRestoreRule {
+        public Default() {
+        }
+
+        @Override
+        public boolean isInclude(PropertyDesc propertyDesc) {
+            return hasAnnocation(propertyDesc, RestoreInclude.class);
+        }
+
+        @Override
+        public boolean isExclude(PropertyDesc propertyDesc) {
+            return hasAnnocation(propertyDesc, RestoreExclude.class);
+        }
+
+        public boolean hasAnnocation(PropertyDesc propertyDesc, Class<? extends Annotation> annotationClass) {
+            if (propertyDesc.getField() != null) {
+                return propertyDesc.getField().getAnnotation(annotationClass) != null;
+            }
+            if (propertyDesc.getReadMethod() != null) {
+                return propertyDesc.getReadMethod().getAnnotation(annotationClass) != null;
+            }
+            return false;
+        }
+    }
+}

sastruts-oreo/src/main/java/jp/troter/seasar/struts/annotation/RestoreExclude.java

+package jp.troter.seasar.struts.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 復元しないプロパティである
+ */
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented()
+public @interface RestoreExclude {
+
+}

sastruts-oreo/src/main/java/jp/troter/seasar/struts/annotation/RestoreInclude.java

+package jp.troter.seasar.struts.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 復元するプロパティである
+ */
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented()
+public @interface RestoreInclude {
+
+}

sastruts-oreo/src/main/java/jp/troter/seasar/struts/resource/Constants.java

+package jp.troter.seasar.struts.resource;
+
+public final class Constants {
+
+    // match enhanced by XXX AOP maker
+    public static final String ENHANCED_BY_XXX_AOP_MAKER_RE = "\\$\\$.*$";
+
+    private Constants() {
+    }
+}

sastruts-oreo/src/test/java/jp/troter/seasar/struts/action/util/ActionFormRestoreTest.java

+package jp.troter.seasar.struts.action.util;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import jp.troter.seasar.struts.annotation.RestoreExclude;
+import jp.troter.seasar.struts.annotation.RestoreInclude;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.seasar.framework.util.tiger.CollectionsUtil;
+
+public class ActionFormRestoreTest extends ActionFormRestore {
+
+    Map<String, Object> store;
+    ActionFormRestore a;
+
+    @Before
+    public void before() throws Exception {
+        store = CollectionsUtil.newHashMap();
+        a = new ActionFormRestore() {
+            @Override
+            protected Map<String, Object> getStore(HttpSession session) {
+                return store;
+            }
+        };
+    }
+
+    public static class TestFormA {
+        public String a;
+        public String b;
+    }
+    @Test
+    public void testParseRule_A() {
+        IncludeAndExclude r = a.parseRule(new TestFormA());
+        assertTrue(r.getIncludes().isEmpty());
+        assertTrue(r.getExcludes().isEmpty());
+    }
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testStore_A() {
+        Map<String, Object> stored = null;
+        TestFormA form = new TestFormA();
+        form.a = "aa";
+        form.b = "bb";
+        a.store("namespace", null, form);
+        assertTrue(store.containsKey("namespace"));
+        stored = (Map<String, Object>)store.get("namespace");
+        assertTrue(stored.containsKey("a"));
+        assertThat(stored.get("a").toString(), is("aa"));
+        assertTrue(stored.containsKey("b"));
+        assertThat(stored.get("b").toString(), is("bb"));
+
+        a.store("namespace", null, form, "a");
+        assertTrue(store.containsKey("namespace"));
+        stored = (Map<String, Object>)store.get("namespace");
+        assertFalse(stored.containsKey("a"));
+        assertTrue(stored.containsKey("b"));
+        assertThat(stored.get("b").toString(), is("bb"));
+
+        a.store("namespace", null, form, "a", "b");
+        assertTrue(store.containsKey("namespace"));
+        stored = (Map<String, Object>)store.get("namespace");
+        assertFalse(stored.containsKey("a"));
+        assertFalse(stored.containsKey("b"));
+    }
+
+    public static class TestFormB {
+        @RestoreInclude
+        public String a;
+        public String b;
+    }
+    @Test
+    public void testParseRule_B() {
+        IncludeAndExclude r = a.parseRule(new TestFormB());
+        assertFalse(r.getIncludes().isEmpty());
+        assertThat(r.getIncludes(), contains("a"));
+        assertTrue(r.getExcludes().isEmpty());
+    }
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testStore_B() {
+        TestFormB form = new TestFormB();
+        form.a = "aa";
+        form.b = "bb";
+        a.store("namespace", null, form);
+        assertTrue(store.containsKey("namespace"));
+        Map<String, Object> stored = (Map<String, Object>)store.get("namespace");
+        assertTrue(stored.containsKey("a"));
+        assertThat(stored.get("a").toString(), is("aa"));
+        assertFalse(stored.containsKey("b"));
+    }
+
+    public static class TestFormC {
+        @RestoreInclude
+        public String a;
+        @RestoreExclude
+        public String b;
+    }
+    @Test
+    public void testParseRule_C() {
+        IncludeAndExclude r = a.parseRule(new TestFormC());
+        assertFalse(r.getIncludes().isEmpty());
+        assertThat(r.getIncludes(), contains("a"));
+        assertFalse(r.getExcludes().isEmpty());
+        assertThat(r.getExcludes(), contains("b"));
+    }
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testStore_C() {
+        TestFormC form = new TestFormC();
+        form.a = "aa";
+        form.b = "bb";
+        a.store("namespace", null, form);
+        assertTrue(store.containsKey("namespace"));
+        Map<String, Object> stored = (Map<String, Object>)store.get("namespace");
+        assertTrue(stored.containsKey("a"));
+        assertThat(stored.get("a").toString(), is("aa"));
+        assertFalse(stored.containsKey("b"));
+    }
+
+    public static class TestFormD {
+        public String a;
+        @RestoreExclude
+        public String b;
+    }
+    @Test
+    public void testParseRule_D() {
+        IncludeAndExclude r = a.parseRule(new TestFormD());
+        assertTrue(r.getIncludes().isEmpty());
+        assertFalse(r.getExcludes().isEmpty());
+        assertThat(r.getExcludes(), contains("b"));
+    }
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testStore_D() {
+        TestFormD form = new TestFormD();
+        form.a = "aa";
+        form.b = "bb";
+        a.store("namespace", null, form);
+        assertTrue(store.containsKey("namespace"));
+        Map<String, Object> stored = (Map<String, Object>)store.get("namespace");
+        assertTrue(stored.containsKey("a"));
+        assertThat(stored.get("a").toString(), is("aa"));
+        assertFalse(stored.containsKey("b"));
+    }
+
+    @Test
+    public void testClassToName() {
+        assertThat(
+            a.classToName("IndexAction"),
+            is("IndexAction"));
+        assertThat(
+            a.classToName("a.b.c.IndexAction"),
+            is("a.b.c.IndexAction"));
+        assertThat(
+            a.classToName("IndexAction$$EnhancedByS2AOP$$6607965.$$init$$invokeSuperMethod$$"),
+            is("IndexAction"));
+        assertThat(
+            a.classToName("a.b.c.IndexAction$$EnhancedByS2AOP$$6607965.$$init$$invokeSuperMethod$$"),
+            is("a.b.c.IndexAction"));
+        assertThat(
+            a.classToName("a.b.c.IndexAction$InnerClass$$EnhancedByS2AOP$$6607965.$$init$$invokeSuperMethod$$"),
+            is("a.b.c.IndexAction$InnerClass"));
+        assertThat(
+            a.classToName("a.b.c.IndexAction$InnerClass$InnerClass$$EnhancedByS2AOP$$6607965.$$init$$invokeSuperMethod$$"),
+            is("a.b.c.IndexAction$InnerClass$InnerClass"));
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.