/*
 * This file or a portion of this file is licensed under the terms of
 * the Globus Toolkit Public License, found in file GTPL, or at
 * http://www.globus.org/toolkit/download/license.html. This notice must
 * appear in redistributions of this file, with or without modification.
 *
 * Redistributions of this Software, with or without modification, must
 * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
 * some other similar material which is provided with the Software (if
 * any).
 *
 * Copyright 1999-2004 University of Chicago and The University of
 * Southern California. All rights reserved.
 */

package edu.isi.ikcap.workflows.util.logging;

/**
 * This class tries to define an interface to deal with quoting, escaping, and the way back. The
 * quoting algorithm is safe to only itself. Thus,
 *
 * <pre>
 * unescape( escape( s ) ) === s
 * </pre>
 *
 * holds true, but
 *
 * <pre>
 * escape( unescape( s ) ) =?= s
 * </pre>
 *
 * does not necessarily hold.
 *
 * @author Gaurang Mehta
 * @author Karan Vahi
 * @author Jens-S. Vöckler
 * @version $Revision: 1.1 $
 */
public class Escape {
    /** Defines the character used to escape characters. */
    private char m_escape;

    /** Defines the set of characters that require escaping. */
    private String m_escapable;

    /**
     * Defines the default quoting and escaping rules, escaping the apostrophe, double quote and
     * backslash. The escape character is the backslash.
     */
    public Escape() {
        m_escapable = "\"'\\";
        m_escape = '\\';
    }

    /**
     * Constructs arbitrary escaping rules.
     *
     * @param escapable is the set of characters that require escaping
     * @param escape is the escape character itself.
     */
    public Escape(String escapable, char escape) {
        this(escapable, escape, true);
    }

    /**
     * Constructs arbitrary escaping rules.
     *
     * @param escapable is the set of characters that require escaping
     * @param escape is the escape character itself.
     * @param escapeEscape boolean indicating whether escape character itself should be escaped if
     *     not present in escapable.
     */
    public Escape(String escapable, char escape, boolean escapeEscape) {
        m_escape = escape;
        m_escapable = escapable;

        if (escapeEscape) {
            // ensure that the escape character is part of the escapable char set
            if (escapable.indexOf(escape) == -1) m_escapable += m_escape;
        }
    }

    /**
     * Transforms a given string by escaping all characters inside the quotable characters set with
     * the escape character. The escape character itself is also escaped.
     *
     * @param s is the string to escape.
     * @return the quoted string
     * @see #unescape( String )
     */
    public String escape(String s) {
        // sanity check
        if (s == null) return null;

        StringBuffer result = new StringBuffer(s.length());
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            if (m_escapable.indexOf(ch) != -1) result.append(m_escape);
            result.append(ch);
        }
        return result.toString();
    }

    /**
     * Transforms a given string by unescaping all characters that are prefixed with the escape
     * character.
     *
     * @param s is the string to remove escapes from.
     * @return the quoted string
     * @see #unescape( String )
     */
    public String unescape(String s) {
        // sanity check
        if (s == null) return null;

        StringBuffer result = new StringBuffer(s.length());
        int state = 0;
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            if (state == 0) {
                // default state
                if (ch == m_escape) state = 1;
                else result.append(ch);
            } else {
                // "found escape" state
                if (m_escapable.indexOf(ch) == -1) result.append(m_escape);
                result.append(ch);
                state = 0;
            }
        }

        return result.toString();
    }

    /**
     * Test program.
     *
     * @param args are command-line arguments
     */
    public static void main(String args[]) {
        Escape me = new Escape(); // defaults

        for (int i = 0; i < args.length; ++i) {
            String e = me.escape(args[i]);
            String u = me.unescape(args[i]);
            System.out.println("raw s  > " + args[i]);
            System.out.println("e(s)   > " + e);
            System.out.println("u(e(s))> " + me.unescape(e));
            System.out.println("u(s)   > " + u);
            System.out.println("e(u(s))> " + me.escape(u));
            System.out.println();
        }
    }
}
