001/*
002// $Id: //open/util/resgen/src/org/eigenbase/xom/StringEscaper.java#6 $
003// Package org.eigenbase.xom is an XML Object Mapper.
004// Copyright (C) 2005-2005 The Eigenbase Project
005// Copyright (C) 2005-2005 Disruptive Tech
006// Copyright (C) 2005-2005 LucidEra, Inc.
007// Portions Copyright (C) 2000-2005 Kana Software, Inc. and others.
008//
009// This library is free software; you can redistribute it and/or modify it
010// under the terms of the GNU Lesser General Public License as published by the
011// Free Software Foundation; either version 2 of the License, or (at your
012// option) any later version approved by The Eigenbase Project.
013//
014// This library is distributed in the hope that it will be useful,
015// but WITHOUT ANY WARRANTY; without even the implied warranty of
016// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017// GNU Lesser General Public License for more details.
018//
019// You should have received a copy of the GNU Lesser General Public License
020// along with this library; if not, write to the Free Software
021// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
022*/
023
024package org.eigenbase.xom;
025import java.util.*;
026
027/**
028 * <p><code>StringEscaper</code> is a utility for replacing special characters
029 * with escape sequences in strings.  Initially, a StringEscaper starts out as
030 * an identity transform in the "mutable" state.  Call defineEscape as many
031 * times as necessary to set up mappings, and then call makeImmutable() before
032 * using appendEscapedString to actually apply the defined transform.  Or, use one of
033 * the global mappings pre-defined here.</p>
034 **/
035public class StringEscaper implements Cloneable
036{
037    private ArrayList translationVector;
038    private String [] translationTable;
039
040    public static StringEscaper xmlEscaper;
041    public static StringEscaper xmlNumericEscaper;
042    public static StringEscaper htmlEscaper;
043    public static StringEscaper urlArgEscaper;
044    public static StringEscaper urlEscaper;
045
046    /**
047     * Identity transform
048     */
049    public StringEscaper()
050    {
051        translationVector = new ArrayList();
052    }
053
054    /**
055     * Map character "from" to escape sequence "to"
056     */
057    public void defineEscape(char from,String to)
058    {
059        int i = (int) from;
060        if (i >= translationVector.size()) {
061            // Extend list by adding the requisite number of nulls.
062            final int count = i + 1 - translationVector.size();
063            translationVector.addAll(
064                new AbstractList() {
065                    public Object get(int index) {
066                        return null;
067                    }
068
069                    public int size() {
070                        return count;
071                    }
072                });
073        }
074        translationVector.set(i, to);
075    }
076
077    /**
078     * Call this before attempting to escape strings; after this,
079     * defineEscape may not be called again.
080     */
081    public void makeImmutable()
082    {
083        translationTable =
084            (String[]) translationVector.toArray(
085                new String[translationVector.size()]);
086        translationVector = null;
087    }
088
089    /**
090     * Apply an immutable transformation to the given string.
091     */
092    public String escapeString(String s)
093    {
094        StringBuffer sb = null;
095        int n = s.length();
096        for (int i = 0; i < n; i++) {
097            char c = s.charAt(i);
098            String escape;
099            // codes >= 128 (e.g. Euro sign) are always escaped
100            if (c > 127) {
101                escape = "&#" + Integer.toString(c) + ";";
102            } else if (c >= translationTable.length) {
103                escape = null;
104            } else {
105                escape = translationTable[c];
106            }
107            if (escape == null) {
108                if (sb != null) {
109                    sb.append(c);
110                }
111            } else {
112                if (sb == null) {
113                    sb = new StringBuffer(n * 2);
114                    sb.append(s.substring(0, i));
115                }
116                sb.append(escape);
117            }
118        }
119
120        if (sb == null) {
121            return s;
122        } else {
123            return sb.toString();
124        }
125    }
126
127    /**
128     * Apply an immutable transformation to the given string, writing the
129     * results to a string buffer.
130     */
131    public void appendEscapedString(String s, StringBuffer sb)
132    {
133        int n = s.length();
134        for (int i = 0; i < n; i++) {
135            char c = s.charAt(i);
136            String escape;
137            if (c >= translationTable.length) {
138                escape = null;
139            } else {
140                escape = translationTable[c];
141            }
142            if (escape == null) {
143                sb.append(c);
144            } else {
145                sb.append(escape);
146            }
147        }
148    }
149
150    protected Object clone()
151    {
152        StringEscaper clone = new StringEscaper();
153        if (translationVector != null) {
154            clone.translationVector = new ArrayList(translationVector);
155        }
156        if (translationTable != null) {
157            clone.translationTable = (String[]) translationTable.clone();
158        }
159        return clone;
160    }
161
162    /**
163     * Create a mutable escaper from an existing escaper, which may
164     * already be immutable.
165     */
166    public StringEscaper getMutableClone()
167    {
168        StringEscaper clone = (StringEscaper) clone();
169        if (clone.translationVector == null) {
170            clone.translationVector =
171                new ArrayList(Arrays.asList(clone.translationTable));
172            clone.translationTable = null;
173        }
174        return clone;
175    }
176
177    static
178    {
179        htmlEscaper = new StringEscaper();
180        htmlEscaper.defineEscape('&', "&amp;");
181        htmlEscaper.defineEscape('"', "&quot;");
182//      htmlEscaper.defineEscape('\'',"&apos;");
183        htmlEscaper.defineEscape('\'', "&#39;");
184        htmlEscaper.defineEscape('<', "&lt;");
185        htmlEscaper.defineEscape('>', "&gt;");
186
187        xmlNumericEscaper = new StringEscaper();
188        xmlNumericEscaper.defineEscape('&',"&#38;");
189        xmlNumericEscaper.defineEscape('"',"&#34;");
190        xmlNumericEscaper.defineEscape('\'',"&#39;");
191        xmlNumericEscaper.defineEscape('<',"&#60;");
192        xmlNumericEscaper.defineEscape('>',"&#62;");
193
194        urlArgEscaper = new StringEscaper();
195        urlArgEscaper.defineEscape('?', "%3f");
196        urlArgEscaper.defineEscape('&', "%26");
197        urlEscaper = urlArgEscaper.getMutableClone();
198        urlEscaper.defineEscape('%', "%%");
199        urlEscaper.defineEscape('"', "%22");
200        urlEscaper.defineEscape('\r', "+");
201        urlEscaper.defineEscape('\n', "+");
202        urlEscaper.defineEscape(' ', "+");
203        urlEscaper.defineEscape('#', "%23");
204
205        htmlEscaper.makeImmutable();
206        xmlEscaper = htmlEscaper;
207        xmlNumericEscaper.makeImmutable();
208        urlArgEscaper.makeImmutable();
209        urlEscaper.makeImmutable();
210    }
211
212}
213
214// End StringEscaper.java