View Javadoc
1   /*
2    * $Id$
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts.util;
22  
23  import java.io.UnsupportedEncodingException;
24  import java.net.URLEncoder;
25  import java.nio.charset.Charset;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * General purpose utility methods related to generating a servlet response in
34   * the Struts controller framework.
35   */
36  public class ResponseUtils {
37  
38      // ------------------------------------------------------- Static Variables
39  
40      /**
41       * The {@code Log} instance for this class.
42       */
43      private static final Logger LOG =
44          LoggerFactory.getLogger(ResponseUtils.class);
45  
46      /**
47       * Pattern to find XML-Entity-Patterns.
48       *
49       * <p>Valid patterns are:</p>
50       *
51       * <ul>
52       * <li>&amp;[a-zA-Z][a-z-A-Z0-9]*; - &amp;amp;, &amp;quot;</li>
53       * <li>&amp;#[0-9]+; - &amp;#32;, &amp;#64;</li>
54       * <li>&amp;#x[a-fA-F0-9]+; - &amp;#x20, &amp;3f</li>
55       * </ul>
56       *
57       * <p>See also <a href="https://www.w3.org/TR/xml11/#sec-references">
58       * XML-Reference 1.1</a>.</p>
59       */
60      private static final Pattern XML_ENTITY_PATTERN =
61              Pattern.compile("&(?:[a-zA-Z][a-zA-Z\\d]*|#\\d+|#x[a-fA-F\\d]+);");
62  
63      /**
64       * The message resources for this package.
65       */
66      protected static MessageResources messages =
67          MessageResources.getMessageResources(
68              "org.apache.struts.util.LocalStrings");
69  
70      // ----------------------------------------------------------- Constructors
71  
72      /**
73       * Class is not instanceable.
74       */
75      protected ResponseUtils() {
76      }
77  
78      // --------------------------------------------------------- Public Methods
79  
80      /**
81       * Filter the specified string for characters that are sensitive to HTML
82       * interpreters, returning the string with these characters replaced by
83       * the corresponding character entities.
84       *
85       * @param value The string to be filtered and returned
86       *
87       * @return String The filtered string
88       */
89      public static String filter(String value) {
90          if (value == null || value.isEmpty()) {
91              return value;
92          }
93  
94          final int length = value.length();
95  
96          StringBuilder result = null;
97          String filtered;
98          Matcher entityMatcher = null;
99  
100         for (int i = 0; i < length; i++) {
101             filtered = null;
102 
103             switch (value.charAt(i)) {
104             case '<':
105                 filtered = "&lt;";
106 
107                 break;
108 
109             case '>':
110                 filtered = "&gt;";
111 
112                 break;
113 
114             case '&':
115                 if (entityMatcher == null) {
116                     entityMatcher = XML_ENTITY_PATTERN.matcher(value);
117                 }
118                 entityMatcher.region(i, length);
119                 if (entityMatcher.lookingAt()) {
120                     filtered = value.substring(entityMatcher.start(), entityMatcher.end());
121                     i += filtered.length() - 1;
122                     if (result == null) {
123                         continue;
124                     }
125                 } else {
126                     filtered = "&amp;";
127                 }
128 
129                 break;
130 
131             case '"':
132                 filtered = "&quot;";
133 
134                 break;
135 
136             case '\'':
137                 filtered = "&#39;";
138 
139                 break;
140 
141              default:
142                     break;
143             }
144 
145             if (result == null) {
146                 if (filtered != null) {
147                     result = new StringBuilder(length + 50);
148 
149                     if (i > 0) {
150                         result.append(value.substring(0, i));
151                     }
152 
153                     result.append(filtered);
154                 }
155             } else {
156                 if (filtered == null) {
157                     result.append(value.charAt(i));
158                 } else {
159                     result.append(filtered);
160                 }
161             }
162         }
163 
164         return result == null ? value : result.toString();
165     }
166 
167     /**
168      * URLencodes a string assuming the character encoding is UTF-8.
169      *
170      * @param url The url to encode
171      *
172      * @return String The encoded url in UTF-8
173      */
174     public static String encodeURL(String url) {
175         return encodeURL(url, "UTF-8");
176     }
177 
178     /**
179      * Use the URLEncoder.encode() with the given encoding. When the
180      * encoding charset didn't found, then it will be tried with the
181      * default-charset.
182      *
183      * @param url The url to encode
184      * @param enc The character encoding the urlencode is performed on.
185      *
186      * @return String The encoded url.
187      */
188     public static String encodeURL(String url, String enc) {
189         String str = null;
190 
191         try {
192             if (enc == null || enc.isEmpty()) {
193                 enc = "UTF-8";
194             }
195 
196             str = URLEncoder.encode(url, enc);
197         } catch (UnsupportedEncodingException e) {
198             LOG.debug("The named encoding is not supported: {}", enc, e);
199 
200             try {
201                 str = URLEncoder.encode(url, Charset.defaultCharset().toString());
202             } catch (UnsupportedEncodingException e1) {
203                 // Should never happen - the system should always have the platform default
204                 LOG.debug("The default-encoding is not supported: {}", enc, e1);
205 
206                 str = url;
207             }
208         }
209 
210         return str;
211     }
212 }