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    
024    package org.eigenbase.xom;
025    import 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     **/
035    public 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