KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > web > util > I18NParseUtil


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 /**
25  * Copyright 2000-2001 by iPlanet/Sun Microsystems, Inc.,
26  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
27  * All rights reserved.
28  */

29
30 package com.sun.enterprise.web.util;
31
32 import java.util.Map JavaDoc;
33 import java.util.Locale JavaDoc;
34 import java.util.LinkedList JavaDoc;
35 import java.util.ListIterator JavaDoc;
36 import java.util.logging.Logger JavaDoc;
37 import java.util.logging.Level JavaDoc;
38 import java.io.UnsupportedEncodingException JavaDoc;
39
40 import javax.servlet.http.HttpServletRequest JavaDoc;
41
42 import org.apache.catalina.util.RequestUtil;
43 import com.sun.enterprise.deployment.runtime.web.LocaleCharsetMap;
44 import com.sun.logging.LogDomains;
45
46 public final class I18NParseUtil {
47
48     /**
49      * The logger to use for logging info about the charset
50      * found from hidden field or locale-charset-map
51      */

52     public static Logger JavaDoc _logger = null;
53
54     /**
55      * This indicates whether debug logging is on or not
56      */

57     private static boolean _debugLog;
58
59     // initialize static variables
60
static {
61         _logger = LogDomains.getLogger(LogDomains.WEB_LOGGER);
62         _debugLog = _logger.isLoggable(Level.FINE);
63     }
64
65     /**
66      * Parse the request parameter according to the locale-charset-info tag
67      * in ias-web.xml
68      *
69      * <p>
70      * <strong>IMPLEMENTATION NOTE</strong>: If hiddenFieldName is null,
71      * get the encoding from lcMap object, and let the original
72      * RequestUtil.parseParameters deal with the parsing. If hiddenFieldName
73      * is not null, first prase queryString to get hidden field value.
74      * if hidden field value is found in the queryString, then buf parsing
75      * will be handled by RequestUtil.parseParameters according to that value.
76      * If hidden field value is not found in queryString, then save the
77      * query string keys in a linked list and look for a hidden field value
78      * in buf. Once the hidden field value is found in buf, process
79      * the linked list of queryString according to that value.
80      * If hidden field value not found in buf either, get encoding value
81      * from lcMap. If none is found, then default to ISO-8859-1.
82      * Then process both linked list objects.
83      *
84      * NOTE: byte array buf is modified by this method. Caller beware.
85      *
86      * @param map Map that accumulates the resulting parameters
87      * @param queryString Input string containing request parameters
88      * @param buf Input byte array containing request parameters
89      * @param lcMap Inputcontainig locale-charset mapping info
90      * @param hiddenFieldName hidden field which will specify the
91      * decoding charset
92      * @param request object used to get the locale and agent header value
93      *
94      * @return The encoding of the request
95      * @exception UnsupportedEncodingException if the data is malformed
96      */

97      public static String JavaDoc parseParametersUsingLCInfo(
98                           Map JavaDoc map, String JavaDoc queryString, byte[] buf,
99                           LocaleCharsetMap[] lcMap, String JavaDoc hiddenFieldName,
100                           HttpServletRequest JavaDoc request)
101         throws UnsupportedEncodingException JavaDoc {
102
103         byte[] queryStringBytes = null;
104         if ((queryString != null) && (queryString.length() > 0))
105             queryStringBytes = queryString.getBytes();
106
107         //process query string
108
LinkedList JavaDoc queryStringKeys = new LinkedList JavaDoc();
109         String JavaDoc hiddenFieldValue = processBufferWithHiddenField(
110                                       map, queryStringBytes, hiddenFieldName,
111                                       queryStringKeys, null, null);
112         //done with query String, process buf
113
//test if hidden field was found in query string
114
if (hiddenFieldValue != null) {
115             if (_debugLog)
116                 _logger.fine("Got charset from queryString, hidden field " +
117                     "name = " + hiddenFieldName + ", hidden field value = " +
118                     hiddenFieldValue);
119             RequestUtil.parseParameters(map, buf, hiddenFieldValue);
120             return hiddenFieldValue;
121         }
122         
123         // hidden field not found in query string, try to find it in POST data
124
LinkedList JavaDoc bufKeys = new LinkedList JavaDoc();
125         hiddenFieldValue = processBufferWithHiddenField(
126                                map, buf, hiddenFieldName, bufKeys,
127                                queryStringBytes, queryStringKeys);
128
129         if (hiddenFieldValue != null) {
130             if (_debugLog)
131                 _logger.fine("Got charset from POST data, hidden field " +
132                     "name = " + hiddenFieldName + ", hidden field value = " +
133                     hiddenFieldValue);
134             return hiddenFieldValue;
135         }
136
137         String JavaDoc encoding = null;
138         if (lcMap != null) {
139             encoding = getLocaleCharsetEncoding(request, lcMap);
140             hiddenFieldValue = encoding;
141         }
142
143         if (encoding == null) {
144             encoding = "ISO-8859-1";
145             if (_debugLog)
146                 _logger.fine("Using default encoding to parse params: " +
147                              encoding);
148         }
149      
150         processLinkedList(map, queryStringBytes, queryStringKeys, encoding);
151         processLinkedList(map, buf, bufKeys, encoding);
152
153         return hiddenFieldValue;
154     }
155
156     /**
157      * Append request parameters from the specified String to the specified
158      * Map. It is presumed that the specified Map is not accessed from any
159      * other thread, so no synchronization is performed.
160      * <p>
161      * <strong>IMPLEMENTATION NOTE</strong>: We look for a hidden field value
162      * in order to do URL decoding according it. While looking for that
163      * hidden field, keys are stored in a linked list along with the start
164      * and end pos of the correspondign value. This is done to avoid double
165      * parsing. Once the hidden field is found, the linked list is processed
166      * and parsing continues according to the already found hidden field value
167      * Parsing is done individually on name and value elements, rather than on
168      * the entire query string ahead of time, to properly deal with the case
169      * where the name or value includes an encoded "=" or "&" character
170      * that would otherwise be interpreted as a delimiter.
171      *
172      * NOTE: byte array data is modified by this method. Caller beware.
173      *
174      * @param map Map that accumulates the resulting parameters
175      * @param hiddenFieldName hidden field which will specify the
176      * decoding charset
177      * @param keys linked list to store the keys
178      *
179      * @return The hidden field value corresponding to hiddenFieldName
180      * @exception UnsupportedEncodingException if the data is malformed
181      */

182     public static String JavaDoc processBufferWithHiddenField(
183                              Map JavaDoc map, byte[] buf, String JavaDoc hiddenFieldName,
184                              LinkedList JavaDoc keys, byte[] queryStringBytes,
185                              LinkedList JavaDoc queryStringKeys)
186         throws UnsupportedEncodingException JavaDoc {
187
188         int pos = 0;
189         int ix = 0;
190         int ox = 0;
191         String JavaDoc key = null;
192         String JavaDoc value = null;
193         boolean foundHiddenField = false;
194         String JavaDoc hiddenFieldValue = null;
195
196         if (buf == null)
197             return null;
198
199         while (ix < buf.length) {
200             byte c = buf[ix++];
201             switch ((char) c) {
202             case '&':
203                 if (key != null) {
204                     if (foundHiddenField) {
205                         value = new String JavaDoc(buf, pos, ox - pos,
206                                            hiddenFieldValue);
207                         putMapEntry(map, key, value);
208                     }
209                     else if (key.equals(hiddenFieldName)) {
210                         hiddenFieldValue = new String JavaDoc(buf, pos, ox - pos,
211                                                       "ISO-8859-1");
212                         // hidden field found, process the linked lists that
213
// have been created so far
214
if (queryStringKeys != null)
215                             processLinkedList(map, queryStringBytes,
216                                          queryStringKeys, hiddenFieldValue);
217                         processLinkedList(map, buf, keys, hiddenFieldValue);
218                         putMapEntry(map, key, hiddenFieldValue);
219                         foundHiddenField = true;
220                     }
221                     else {
222                         Object JavaDoc[] startEndPos = new Object JavaDoc[3];
223                         startEndPos[0] = key;
224                         startEndPos[1] = new Integer JavaDoc(pos);
225                         startEndPos[2] = new Integer JavaDoc(ox);
226                         keys.add(startEndPos);
227                     }
228                     key = null;
229                 }
230                 pos = ix;
231                 ox = ix;
232                 break;
233             case '=':
234                 if (key == null) {
235                     key = new String JavaDoc(buf, pos, ox - pos, "ISO-8859-1");
236                     ox = ix;
237                     pos = ix;
238                 } else {
239                     buf[ox++] = c;
240                 }
241                 break;
242             case '+':
243                 buf[ox++] = (byte)' ';
244                 break;
245             case '%':
246                 buf[ox++] = (byte)((convertHexDigit(buf[ix++]) << 4) +
247                                     convertHexDigit(buf[ix++]));
248                 break;
249             default:
250                 buf[ox++] = c;
251             }
252         }
253
254         //The last value does not end in '&'. So save it now.
255
if (key != null) {
256             if (foundHiddenField) {
257                 value = new String JavaDoc(buf, pos, ox - pos, hiddenFieldValue);
258                 putMapEntry(map, key, value);
259             }
260             else if (key.equals(hiddenFieldName)) {
261                 hiddenFieldValue = new String JavaDoc(buf, pos, ox - pos, "ISO-8859-1");
262                 // hidden field found, process the linked lists that
263
// have been created so far
264
if (queryStringKeys != null)
265                     processLinkedList(map, queryStringBytes, queryStringKeys,
266                                       hiddenFieldValue);
267                 processLinkedList(map, buf, keys, hiddenFieldValue);
268                 putMapEntry(map, key, hiddenFieldValue);
269                 foundHiddenField = true;
270             }
271             else {
272                 Object JavaDoc[] startEndPos = new Object JavaDoc[3];
273                 startEndPos[0] = key;
274                 startEndPos[1] = new Integer JavaDoc(pos);
275                 startEndPos[2] = new Integer JavaDoc(ox);
276                 keys.add(startEndPos);
277             }
278         }
279
280         return hiddenFieldValue;
281     }
282
283     /**
284      * Process a linked list containing. The linked list nodes consist of
285      * (key, start pos, end pos)
286      * start pos and end pos correspond to the start and end postion of the
287      * value corresponding to the key.
288      * <p>
289      * <strong>IMPLEMENTATION NOTE</strong>: Process the whole linked.
290      * A key, value pair is created from each node and stored in the map.
291      *
292      * @param map Map that accumulates the resulting parameters
293      * @param buf array of bytes to be precessed
294      * @param keys linked listcontaning the keys
295      * @param encoding charset for converting bytes to java strings
296      *
297      * @exception UnsupportedEncodingException if the data is malformed
298      */

299     public static void processLinkedList(Map JavaDoc map, byte[] buf, LinkedList JavaDoc keys,
300                                          String JavaDoc encoding)
301         throws UnsupportedEncodingException JavaDoc {
302
303         if (buf == null || keys == null)
304             return;
305
306         ListIterator JavaDoc keysIterator = keys.listIterator(0);
307         while (keysIterator.hasNext()) {
308             Object JavaDoc[] startEndPos = (Object JavaDoc[])keysIterator.next();
309             String JavaDoc key = (String JavaDoc)startEndPos[0];
310             int startPos = ((Integer JavaDoc)startEndPos[1]).intValue();
311             int endPos = ((Integer JavaDoc)startEndPos[2]).intValue();
312             String JavaDoc value = new String JavaDoc(buf, startPos, endPos - startPos,
313                                       encoding);
314             putMapEntry(map, key, value);
315         }
316         keys.clear();
317     }
318
319     /**
320      * Return the charset corresponding to a (locale, agent) pair.
321      * <p>
322      * <strong>IMPLEMENTATION NOTE</strong>: The lcMap parameter contains
323      * an array of LocaleCharsetMap object. A LocaleCharsetMap object consist
324      * of (locale, agent, charset). The agent attribute may be a null value.
325      * If agent is null, we match the locale attribute to the requst locale.
326      * If agent is not null we match (locale, agent) to the request locale,
327      * and request agent header value. We look for exact match of the agent,
328      * so it is the user responsibility to give and exact value of the agent
329      * in ias-web XML file. If no match is found, ISO-8859-1 is returned.
330      *
331      * @param request The request object for which a matching charset
332      * is looked for
333      * @param lcMap an object representing locale-charset-map in
334      * sun-web.xml file
335      * @return matching encoding or ISO-8859-1 if no match is found
336      */

337     public static String JavaDoc getLocaleCharsetEncoding(HttpServletRequest JavaDoc request,
338                                          LocaleCharsetMap[] charsetMap) {
339         if (charsetMap == null)
340             return null;
341
342         String JavaDoc requestLocale = request.getLocale().toString();
343         if (requestLocale == null)
344             return null;
345
346         String JavaDoc userAgent = request.getHeader("user-agent");
347
348         for (int i = 0; i < charsetMap.length; i++) {
349             LocaleCharsetMap element = charsetMap[i];
350             String JavaDoc s = (String JavaDoc)element.getAttributeValue("Locale");
351             if (s.equals(requestLocale)) {
352                 String JavaDoc agent = (String JavaDoc)element.getAttributeValue("Agent");
353                 if (agent == null || agent.length() == 0 ||
354                     userAgent == null || agent.equals(userAgent)) {
355                     String JavaDoc encoding =
356                                (String JavaDoc)element.getAttributeValue("Charset");
357                     if (_debugLog)
358                         _logger.fine("Got charset in locale-charset-map" +
359                                      ", locale=" + requestLocale +
360                                      ", agent=" + agent +
361                                      ", charset=" + encoding);
362                     return encoding;
363                 }
364             }
365         }
366         return null;
367     }
368
369     /**
370      * Put name value pair in map.
371      *
372      * @param b the character value byte
373      *
374      * Put name and value pair in map. When name already exist, add value
375      * to array of values.
376      */

377     private static void putMapEntry(Map JavaDoc map, String JavaDoc name, String JavaDoc value) {
378         String JavaDoc[] newValues = null;
379         String JavaDoc[] oldValues = (String JavaDoc[]) map.get(name);
380         if (oldValues == null) {
381             newValues = new String JavaDoc[1];
382             newValues[0] = value;
383         } else {
384             newValues = new String JavaDoc[oldValues.length + 1];
385             System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
386             newValues[oldValues.length] = value;
387         }
388         map.put(name, newValues);
389     }
390
391     /**
392      * Convert a byte character value to hexidecimal digit value.
393      *
394      * @param b the character value byte
395      */

396     private static byte convertHexDigit( byte b ) {
397         if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
398         if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
399         if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
400         return 0;
401     }
402 }
403
Popular Tags