All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.xml.MarkupBuilder Maven / Gradle / Ivy

There is a newer version: 1.5.8
Show newest version
/*
 * Copyright 2003-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package groovy.xml;

import groovy.util.BuilderSupport;
import groovy.util.IndentPrinter;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;

/**
 * A helper class for creating XML or HTML markup
 * 
 * @author James Strachan
 * @author Stefan Matthias Aust
 * @author Scott Stirling
 * @version $Revision: 9546 $
 */
public class MarkupBuilder extends BuilderSupport {
    private IndentPrinter out;
    private boolean nospace;
    private int state;
    private boolean nodeIsEmpty = true;
    private boolean useDoubleQuotes = false;

    public MarkupBuilder() {
        this(new IndentPrinter());
    }

    public MarkupBuilder(PrintWriter writer) {
        this(new IndentPrinter(writer));
    }

    public MarkupBuilder(Writer writer) {
        this(new IndentPrinter(new PrintWriter(writer)));
    }

    public MarkupBuilder(IndentPrinter out) {
        this.out = out;
    }

    /**
     * Returns true if attribute values are output with
     * double quotes; false if single quotes are used.
     * By default, single quotes are used.
     * @return true if double quotes are used for attributes
     */
    public boolean getDoubleQuotes() {
        return this.useDoubleQuotes;
    }

    /**
     * Sets whether the builder outputs attribute values in double
     * quotes or single quotes.
     * @param useDoubleQuotes If this parameter is true,
     * double quotes are used; otherwise, single quotes are.
     */
    public void setDoubleQuotes(boolean useDoubleQuotes) {
        this.useDoubleQuotes = useDoubleQuotes;
    }

    protected IndentPrinter getPrinter() {
        return this.out;
    }

    protected void setParent(Object parent, Object child) { }

    public Object getMkp() {
        return this;
    }

    public void yield(String value) {
        if (state == 1) {
            state = 2;
            this.nodeIsEmpty = false;
            out.print(">");
        }
        if (state == 2 || state == 3) {
            out.print(escapeElementContent(value));
        }
    }

    public void yieldUnescaped(String value) {
        if (state == 1) {
            state = 2;
            this.nodeIsEmpty = false;
            out.print(">");
        }
        if (state == 2 || state == 3) {
            out.print(value);
        }
    }

    protected Object createNode(Object name) {
        this.nodeIsEmpty = true;
        toState(1, name);
        return name;
    }

    protected Object createNode(Object name, Object value) {
        toState(2, name);
        this.nodeIsEmpty = false;
        out.print(">");
        out.print(escapeElementContent(value.toString()));
        return name;
    }

    protected Object createNode(Object name, Map attributes, Object value) {
        toState(1, name);
        for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry) iter.next();
            Object attributeValue = entry.getValue();
            if (attributeValue != null) {
                out.print(" ");
                // Output the attribute name,
                print(entry.getKey().toString());
                // Output the attribute value within quotes. Use whichever
                // type of quotes are currently configured.
                out.print(this.useDoubleQuotes ? "=\"" : "='");
                print(escapeAttributeValue(attributeValue.toString()));
                out.print(this.useDoubleQuotes ? "\"" : "'");
            }
        }

        if (value != null) {
            yield(value.toString());
        } else {
            nodeIsEmpty = true;
        }

        return name;
    }

    protected Object createNode(Object name, Map attributes) {
        return createNode(name, attributes, null);
    }
    
    protected void nodeCompleted(Object parent, Object node) {
        toState(3, node);
        out.flush();
    }

    protected void print(Object node) {
        out.print(node == null ? "null" : node.toString());
    }

    protected Object getName(String methodName) {
        return super.getName(methodName);
    }

    /**
     * Returns a String with special XML characters escaped as entities so that
     * output XML is valid. Escapes the following characters as corresponding 
     * entities:
     * 
    *
  • \' as '
  • *
  • & as &
  • *
  • < as &lt;
  • *
  • > as &gt;
  • *
* * @param value to be searched and replaced for XML special characters. * @return value with XML characters escaped * @deprecated * @see #escapeXmlValue(String, boolean) */ protected String transformValue(String value) { // & has to be checked and replaced before others if (value.matches(".*&.*")) { value = value.replaceAll("&", "&"); } if (value.matches(".*\\'.*")) { value = value.replaceAll("\\'", "'"); } if (value.matches(".*<.*")) { value = value.replaceAll("<", "<"); } if (value.matches(".*>.*")) { value = value.replaceAll(">", ">"); } return value; } /** * Escapes a string so that it can be used directly as an XML * attribute value. * @param value The string to escape. * @return A new string in which all characters that require escaping * have been replaced with the corresponding XML entities. * @see #escapeXmlValue(String, boolean) */ private String escapeAttributeValue(String value) { return escapeXmlValue(value, true); } /** * Escapes a string so that it can be used directly in XML element * content. * @param value The string to escape. * @return A new string in which all characters that require escaping * have been replaced with the corresponding XML entities. * @see #escapeXmlValue(String, boolean) */ private String escapeElementContent(String value) { return escapeXmlValue(value, false); } /** * Escapes a string so that it can be used in XML text successfully. * It replaces the following characters with the corresponding XML * entities: *
    *
  • & as &amp;
  • *
  • < as &lt;
  • *
  • > as &gt;
  • *
* If the string is to be added as an attribute value, these * characters are also escaped: *
    *
  • ' as &apos;
  • *
* @param value The string to escape. * @param isAttrValue true if the string is to be used * as an attribute value, otherwise false. * @return A new string in which all characters that require escaping * have been replaced with the corresponding XML entities. */ private String escapeXmlValue(String value, boolean isAttrValue) { StringBuffer buffer = new StringBuffer(value); for (int i = 0, n = buffer.length(); i < n; i++) { switch (buffer.charAt(i)) { case '&': buffer.replace(i, i + 1, "&"); // We're replacing a single character by a string of // length 5, so we need to update the index variable // and the total length. i += 4; n += 4; break; case '<': buffer.replace(i, i + 1, "<"); // We're replacing a single character by a string of // length 4, so we need to update the index variable // and the total length. i += 3; n += 3; break; case '>': buffer.replace(i, i + 1, ">"); // We're replacing a single character by a string of // length 4, so we need to update the index variable // and the total length. i += 3; n += 3; break; case '"': // The double quote is only escaped if the value is for // an attribute and the builder is configured to output // attribute values inside double quotes. if (isAttrValue && this.useDoubleQuotes) { buffer.replace(i, i + 1, """); // We're replacing a single character by a string of // length 6, so we need to update the index variable // and the total length. i += 5; n += 5; } break; case '\'': // The apostrophe is only escaped if the value is for an // attribute, as opposed to element content, and if the // builder is configured to surround attribute values with // single quotes. if (isAttrValue && !this.useDoubleQuotes){ buffer.replace(i, i + 1, "'"); // We're replacing a single character by a string of // length 6, so we need to update the index variable // and the total length. i += 5; n += 5; } break; default: break; } } return buffer.toString(); } private void toState(int next, Object name) { switch (state) { case 0: switch (next) { case 1: case 2: out.print("<"); print(name); break; case 3: throw new Error(); } break; case 1: switch (next) { case 1: case 2: out.print(">"); if (nospace) { nospace = false; } else { out.println(); out.incrementIndent(); out.printIndent(); } out.print("<"); print(name); break; case 3: if (nodeIsEmpty) { out.print(" />"); } break; } break; case 2: switch (next) { case 1: case 2: out.print("<"); print(name); break; case 3: out.print(""); break; } break; case 3: switch (next) { case 1: case 2: if (nospace) { nospace = false; } else { out.println(); out.printIndent(); } out.print("<"); print(name); break; case 3: if (nospace) { nospace = false; } else { out.println(); out.decrementIndent(); out.printIndent(); } out.print(""); break; } break; } state = next; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy