代码之家  ›  专栏  ›  技术社区  ›  Christopher Schultz

如何使用commons digester调用带有弹出对象的方法?

  •  2
  • Christopher Schultz  · 技术社区  · 7 年前

    <!-- language: xml -->
    <items>
      <item type="java.lang.Boolean" name="foo" value="true" />
    </items>
    

    我想要 <root> 要创建的元素 java.util.Map 对象并拥有每个 <item> 元素创建适当类型的对象,然后向 Map --类似于 SetNextRule 但是调用的参数来自堆栈。

    我已经创建了一个自定义 Rule 将创建在 type 属性( java.lang.Boolean 在这种情况下)使用 value

    现在,我想从堆栈顶部弹出该项,并将其用作 put 上的方法 地图 对象(刚好位于 Boolean 对象)。

    以下是我迄今为止编写的代码:

    <!-- language: lang-java -->
    Digester digester = new Digester();
    digester.addObjectCreate("items", HashMap.class);
    digester.addRule(new MyObjectCreateRule()); // This knows how to create e.g. java.lang.Boolean objects
    digester.addCallMethod("items/item", "put", 2, new Class<?>[] { String.class, Object.class });
    digester.addCallParam("items/item", 0, "name");
    digester.addCallParam("items/item", 1, true); // take argument from stack
    

    我得到一个错误,该方法 在中找不到 Java语言lang.布尔值 班所以,问题是,例如。 布尔型 对象位于堆栈顶部,我想将其用作 在堆栈顶部元素旁边调用的方法:

    堆栈:

    java.lang.Boolean value=true     <-- top of stack, desired call param
    java.util.HashMap contents = {}  <-- desired call target
    

    有没有办法用现有的commons digester规则做到这一点,或者我必须创建另一个自定义规则来执行这种类型的操作?

    2 回复  |  直到 7 年前
        1
  •  0
  •   Christopher Schultz    7 年前

    最后,我编写了一个结合了这两个操作的自定义规则:构造属性值的新实例 将其插入属性包。

    这是对 真实的 我有一个用例,所以代码可能不是100%完美,因为我在这里复制/粘贴并修改了它。我也理解使用属性值而不是 java.lang.String 这不太合理,但它适用于我的用例(它不使用 java.util.Properties ,实际上,但那节课是一个很好的类比)。

    <!-- language: lang-java -->
    /**
     * Implements a create-object-set-property Digester rule.
     */
    public class SetPropertyRule
        extends Rule
    {
        private String _classAttributeName;
        private String _nameAttributeName;
        private String _valueAttributeName;
        private HashSet<String> _acceptableClassNames;
    
        /**
         * Creates a new SetPreferenceRule with default attribute names and classes.
         *
         * Default class attribute name = "type".
         * Default name attribute name = "name".
         * Default value attribute name = "value".
         * Default allowed classes = String, Integer, Double, and Boolean.
         */
        public SetPropertiesRule()
        {
            this("type", "name", "value",
                 new Class<?>[] { String.class, Integer.class, Double.class, Boolean.class });
        }
    
        /**
         * Creates a new SetPropertyRule to construct a name/value pair and
         * set it on a Properties object.
         *
         * The Properties object should be at the top of the current
         * Digester stack.
         *
         * @param classAttributeName The name of the attribute that holds the property's value type.
         * @param nameAttributeName The name of the attribute that holds the property's name.
         * @param valueAttributeName The name of the attribute that holds the property's value.
         * @param acceptableClasses The list of acceptable property value types.
         */
        public SetPreferenceRule(String classAttributeName, String nameAttributeName, String valueAttributeName, Class<?>[] acceptableClasses)
        {
            super();
    
            _classAttributeName = classAttributeName;
            _nameAttributeName = nameAttributeName;
            _valueAttributeName = valueAttributeName;
            _acceptableClassNames = new HashSet<String>(acceptableClasses.length);
            for(Class<?> clazz : acceptableClasses)
                _acceptableClassNames.add(clazz.getName());
        }
    
        @Override
        public void begin(String namespace,
                          String name,
                          Attributes attributes)
            throws Exception
        {
            // Store the values of these attributes on the digester param stack
            getDigester().pushParams(
                    attributes.getValue(_classAttributeName),
                    attributes.getValue(_nameAttributeName),
                    attributes.getValue(_valueAttributeName)
            );
        }
    
        @Override
        public void end(String namespace,
                        String name)
            throws Exception
        {
            Object[] attributeValues = getDigester().popParams();
    
            Object props = getDigester().peek();
            if(!(props instanceof java.util.Properties))
            {
                String typeName;
                if(null == props)
                    typeName = "<null>";
                else
                    typeName = props.getClass().getName();
    
                throw new IllegalStateException("Expected instance of " + Properties.class.getName() + ", got " + typeName + " instead");
            }
    
            String className = (String)attributeValues[0];
            checkClassName(className);
    
            // Create an instance of the preference value class
            Class<?> clazz = Class.forName(className);
            Constructor<?> cons = clazz.getConstructor(String.class);
            Object value = cons.newInstance((String)attributeValues[2]);
    
            ((Properties)props).put((String)attributeValues[1], value);
        }
    
        private void checkClassName(String className)
        {
            if(!_acceptableClassNames.contains(className))
                throw new IllegalArgumentException("Class " + className + " is not allowed");
        }
    }
    

    然而,我很高兴发现有一种现成的方法可以做到这一点。

        2
  •  0
  •   Barney    7 年前

    对于另一种方法,您可以将问题从摘要本身转移出来,并使用增强的map类来提供与现有摘要规则更兼容的方法:

    public static class MyHashMap extends HashMap {
      public Object put(String clazz, String name, String value) {
        Object obj = ... // create object from clazz/name/value
        return super.put(name, obj);
      }
    }
    

    然后只使用现有的 addCallMethod / addCallParam 规则:

    Digester digester = new Digester();
    digester.addObjectCreate("items", MyHashMap.class);
    digester.addCallMethod("items/item", "put", 3, new Class<?>[] { String.class, String.class, String.class });
    digester.addCallParam("items/item", 0, "type");
    digester.addCallParam("items/item", 1, "name");
    digester.addCallParam("items/item", 2, "value");
    

    如果你需要一个纯净的 HashMap 因此,您可以使用类似的方法包装本机类,而不是使用自定义类 哈希图 ,例如。 com.google.common.collect.ForwardingMap 如果你在用番石榴。