On Wed, 2003-11-26 at 09:52, robert burrell donkin wrote: > On 24 Nov 2003, at 23:54, Simon Kitching wrote: > > I'm *really* in favour of providing expansion in body text as well as > > attributes. > > yep. but i'd favour a sequential approach. remy needs attributes right > now, so let's do attributes right now :) > > > I also personally favour an interface rather than an abstract class. > > Who > > knows what implementations people might come up with in the future : > > retrieve values from LDAP? support emacs macros in variables?. > > If the "replacement strategy" is an abstract class that brings > > implementation baggage with it, then it might be more difficult to > > implement those... > > this would be an abstract class that is (from a higher OOP perspective) > an interface but will be implemented in java as an abstract class. it > will contain no implementation. most folks will probably subclass the > base implementation. > > > On the other hand, an abstract class *does* allow us to expand the > > "interface" later by adding operations with default implementations, > > without breaking code. I personally still feel more comfortable with a > > pure interface in this case, though. > > IMHO being able to maintain backwards compatibility is crucial for a > good library. unless i can see the need for different inheritance > hierarchies to implement the interface (which is more common in > frameworks than library's), experience has taught me that (when > developing library code) the flexibility that implementing as an > abstract class gives in java pays dividends in long run.
Attached is a complete implementation of the "variable expansion" feature. I do also have some unit tests for it, but unfortunately they are still on my home PC so I will have to send them tomorrow. This code implements the "multiple sources, configurable var marker" features I talked about earlier. The implementation came together so well that I am not sure that there is any point in trying to provide an alternative "simpler" implementation that just supports one source and "$" as the marker. If you're happy with this, class MultiVarExpander could just be renamed to BaseVarExpander or similar. Preferring abstract classes over interfaces is a new "pattern" to me, but I am willing to give it a go. Class VarExpander is therefore an abstract class rather than an interface. Note that this implementation does almost *no* dynamic memory allocation at all, and therefore should perform very well. Only one "attributes wrapper" object exists per Digester instance; it is reused each time startElement(...) is called. I can't think of any reason why startElement would be called re-entrantly, so this should be safe. It also implements expansion of text in body text (adds about 2 extra lines of code). Regards, Simon > > - robert > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > >
Index: src/java/org/apache/commons/digester/Digester.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/digester/src/java/org/apache/commons/digester/Digester.java,v retrieving revision 1.86 diff -u -r1.86 Digester.java --- src/java/org/apache/commons/digester/Digester.java 19 Nov 2003 21:05:19 -0000 1.86 +++ src/java/org/apache/commons/digester/Digester.java 25 Nov 2003 21:57:45 -0000 @@ -360,6 +360,12 @@ protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; + /** a map which is looked up to find values for variables */ + private VarExpander varExpander = null; + + /** the attributes wrapper (single instance) */ + private VarAttributes varAttributes = new VarAttributes(); + // ------------------------------------------------------------- Properties @@ -935,6 +941,14 @@ } + /** + * Defines how variables in xml attributes and body text are expanded. + * If null, no expansion is performed (the default behaviour). + */ + public void setVarExpander(VarExpander expander) { + varExpander = expander; + } + // ------------------------------------------------- ContentHandler Methods @@ -1038,6 +1052,10 @@ List rules = getRules().match(namespaceURI, match); if ((rules != null) && (rules.size() > 0)) { String bodyText = this.bodyText.toString(); + if (varExpander != null) { + bodyText = varExpander.expand(bodyText); + } + for (int i = 0; i < rules.size(); i++) { try { Rule rule = (Rule) rules.get(i); @@ -1283,6 +1301,13 @@ List rules = getRules().match(namespaceURI, match); if ((rules != null) && (rules.size() > 0)) { String bodyText = this.bodyText.toString(); + + // variable substitution in attributes + if (varExpander != null) { + varAttributes.init(list, varExpander); + list = varAttributes; + } + for (int i = 0; i < rules.size(); i++) { try { Rule rule = (Rule) rules.get(i);
/* * $Header: $ * $Revision: $ * $Date: $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgement may appear in the software itself, * if and wherever such third-party acknowledgements normally appear. * * 4. The names "Apache", "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", * "Apache" nor may "Apache" appear in their names without prior * written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.digester; import java.util.Map; import java.util.ArrayList; /** * <p>Expands variable references from multiple sources.</p> * * @author Simon Kitching * @version $Revision: $ $Date: $ */ public class MultiVarExpander extends VarExpander { private int nEntries = 0; private ArrayList markers = new ArrayList(2); private ArrayList sources = new ArrayList(2); public MultiVarExpander() { } public void addSource(String marker, Map source) { ++nEntries; markers.add(marker); sources.add(source); } /* * Expands any variable declarations using any of the known * variable marker strings. * * @throws IllegalArgumentException if the input param references * a variable which is not known to the specified source. */ public String expand(String param) { for(int i=0; i<nEntries; ++i) { param = expand( param, (String) markers.get(i), (Map) sources.get(i)); } return param; } /** * Replace any occurrences within the string of the form * "marker{key}" with the value from source[key]. * <p> * Commonly, the variable marker is "$", in which case variables * are indicated by ${key} in the string. * <p> * Returns the string after performing all substitutions. * <p> * If no substitutions were made, the input string object is * returned (not a copy). * * @throws IllegalArgumentException if the input param references * a variable which is not known to the specified source. */ public String expand(String str, String marker, Map source) { String startMark = marker + "{"; int markLen = startMark.length(); int index = 0; for(;;) { index = str.indexOf(startMark, index); if (index == -1) { return str; } int startIndex = index + markLen; if (startIndex > str.length()) { throw new IllegalArgumentException( "var expression starts at end of string"); } int endIndex = str.indexOf("}", index + markLen); if (endIndex == -1) { throw new IllegalArgumentException( "var expression starts but does not end"); } String key = str.substring(index+markLen, endIndex); Object value = source.get(key); if (value == null) { throw new IllegalArgumentException( "parameter [" + key + "] is not defined."); } String varValue = value.toString(); str = str.substring(0, index) + varValue + str.substring(endIndex+1); index += varValue.length(); } } }
/* * $Header: $ * $Revision: $ * $Date: $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgement may appear in the software itself, * if and wherever such third-party acknowledgements normally appear. * * 4. The names "Apache", "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", * "Apache" nor may "Apache" appear in their names without prior * written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.digester; import org.xml.sax.Attributes; import java.util.Map; import java.util.ArrayList; /** * <p>Wrapper for an org.xml.sax.Attributes object which expands any * "variables" referenced in the attribute value via ${foo} or similar. * This is only done something actually asks for the attribute value, * thereby imposing no performance penalty if the attribute is not used.</p> * * @author Simon Kitching * @version $Revision: $ $Date: $ */ public class VarAttributes implements Attributes { // list of mapped attributes. private ArrayList values = new ArrayList(10); private Attributes attrs; private VarExpander expander; // ------------------- Public Methods /** * Specify which attributes class this object is a proxy for. */ public void init(Attributes attrs, VarExpander expander) { this.attrs = attrs; this.expander = expander; // I hope this doesn't release the memory for this array; for // efficiency, this should just mark the array as being size 0. values.clear(); } public String getValue(int index) { if (index >= values.size()) { // Expand the values array with null elements, so the later // call to set(index, s) works ok. // // Unfortunately, there is no easy way to set the size of // an arraylist; we must repeatedly add null elements to it.. values.ensureCapacity(index+1); for(int i = values.size(); i<= index; ++i) { values.add(null); } } String s = (String) values.get(index); if (s == null) { // we have never been asked for this value before. // get the real attribute value and perform substitution // on it. s = attrs.getValue(index); if (s != null) { s = expander.expand(s); values.set(index, s); } } return s; } public String getValue(String qname) { int index = attrs.getIndex(qname); if (index == -1) { return null; } return getValue(index); } public String getValue(String uri, String localname) { int index = attrs.getIndex(uri, localname); if (index == -1) { return null; } return getValue(index); } // plain proxy methods follow : nothing interesting :-) public int getIndex(String qname) { return attrs.getIndex(qname); } public int getIndex(String uri, String localpart) { return attrs.getIndex(uri, localpart); } public int getLength() { return attrs.getLength(); } public String getLocalName(int index) { return attrs.getLocalName(index); } public String getQName(int index) { return attrs.getQName(index); } public String getType(int index) { return attrs.getType(index); } public String getType(String qname) { return attrs.getType(qname); } public String getType(String uri, String localname) { return attrs.getType(uri, localname); } public String getURI(int index) { return attrs.getURI(index); } }
/* * $Header: $ * $Revision: $ * $Date: $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgement may appear in the software itself, * if and wherever such third-party acknowledgements normally appear. * * 4. The names "Apache", "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", * "Apache" nor may "Apache" appear in their names without prior * written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ /** * <p>An Interface describing a class capable of expanding strings which * may contain variable references. The exact syntax of the "reference", * and the mechanism for determining the corresponding value to be used * is up to the concrete implementation.</p> * * @author Simon Kitching * @version $Revision: $ $Date: $ */ package org.apache.commons.digester; public abstract class VarExpander { /** * Return the input string with any variables replaced by their * corresponding value. If there are no variables in the string, * then the input parameter is returned unaltered. */ abstract public String expand(String param); }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]