| 1 |  |   | 
  | 2 |  |   | 
  | 3 |  |   | 
  | 4 |  |   | 
  | 5 |  |   | 
  | 6 |  |   | 
  | 7 |  |   | 
  | 8 |  |   | 
  | 9 |  |   | 
  | 10 |  |   | 
  | 11 |  |   | 
  | 12 |  |   | 
  | 13 |  |   | 
  | 14 |  |   | 
  | 15 |  |   | 
  | 16 |  |  package org.seasar.teeda.core.context.html; | 
  | 17 |  |   | 
  | 18 |  |  import java.io.IOException; | 
  | 19 |  |  import java.io.Writer; | 
  | 20 |  |  import java.net.URLEncoder; | 
  | 21 |  |   | 
  | 22 |  |  import javax.faces.component.UIComponent; | 
  | 23 |  |  import javax.faces.context.ResponseWriter; | 
  | 24 |  |   | 
  | 25 |  |  import org.seasar.framework.util.ArrayUtil; | 
  | 26 |  |  import org.seasar.framework.util.AssertionUtil; | 
  | 27 |  |  import org.seasar.framework.util.StringUtil; | 
  | 28 |  |  import org.seasar.teeda.core.JsfConstants; | 
  | 29 |  |  import org.seasar.teeda.core.util.EmptyElementUtil; | 
  | 30 |  |  import org.seasar.teeda.core.util.HTMLEncodeUtil; | 
  | 31 |  |   | 
  | 32 |  |   | 
  | 33 |  |   | 
  | 34 |  |   | 
  | 35 |  |   | 
  | 36 |  |   | 
  | 37 |  |   | 
  | 38 | 429 |  public class HtmlResponseWriter extends ResponseWriter { | 
  | 39 |  |   | 
  | 40 | 1 |      private static final char[] reserved = { ';', '/', '?', ':', '@', '&', '=', | 
  | 41 |  |              '+', '$', ',' }; | 
  | 42 |  |   | 
  | 43 |  |      private static final char[] unescape; | 
  | 44 |  |   | 
  | 45 |  |      static { | 
  | 46 | 1 |          unescape = new char[reserved.length + 1]; | 
  | 47 | 1 |          System.arraycopy(reserved, 0, unescape, 0, reserved.length); | 
  | 48 | 1 |          unescape[unescape.length - 1] = '#'; | 
  | 49 | 1 |      } | 
  | 50 |  |   | 
  | 51 |  |      private Writer writer; | 
  | 52 |  |   | 
  | 53 |  |      private String contentType; | 
  | 54 |  |   | 
  | 55 |  |      private String characterEncoding; | 
  | 56 |  |   | 
  | 57 |  |      private boolean startTagOpening; | 
  | 58 |  |   | 
  | 59 | 429 |      private boolean shouldEscape = DEFAULT_ESCAPE; | 
  | 60 |  |   | 
  | 61 |  |      private static final boolean DEFAULT_ESCAPE = true; | 
  | 62 |  |   | 
  | 63 |  |      public void startElement(final String name, | 
  | 64 |  |              final UIComponent componentForElement) throws IOException { | 
  | 65 | 870 |          AssertionUtil.assertNotNull("name", name); | 
  | 66 | 869 |          final Writer writer = getWriter(); | 
  | 67 | 869 |          closeStartTagIfOpening(writer); | 
  | 68 | 869 |          writer.write("<"); | 
  | 69 | 869 |          writer.write(name); | 
  | 70 |  |           | 
  | 71 | 869 |          if ("script".equalsIgnoreCase(name)) { | 
  | 72 | 7 |              shouldEscape = false; | 
  | 73 |  |          } else { | 
  | 74 | 862 |              shouldEscape = true; | 
  | 75 |  |          } | 
  | 76 | 869 |          startTagOpening = true; | 
  | 77 | 869 |      } | 
  | 78 |  |   | 
  | 79 |  |      protected void closeStartTagIfOpening(final Writer writer) | 
  | 80 |  |              throws IOException { | 
  | 81 | 1306 |          if (startTagOpening) { | 
  | 82 | 691 |              writer.write(">"); | 
  | 83 | 691 |              startTagOpening = false; | 
  | 84 |  |          } | 
  | 85 | 1306 |      } | 
  | 86 |  |   | 
  | 87 |  |      public void endElement(final String name) throws IOException { | 
  | 88 | 806 |          AssertionUtil.assertNotNull("name", name); | 
  | 89 | 805 |          final Writer writer = getWriter(); | 
  | 90 | 805 |          if (startTagOpening) { | 
  | 91 | 156 |              if (EmptyElementUtil.isEmptyElement(name)) { | 
  | 92 | 118 |                  writer.write(" />"); | 
  | 93 |  |              } else { | 
  | 94 | 38 |                  writer.write(">"); | 
  | 95 | 38 |                  writer.write("</" + name + ">"); | 
  | 96 |  |              } | 
  | 97 | 156 |              startTagOpening = false; | 
  | 98 |  |          } else { | 
  | 99 | 649 |              writer.write("</" + name + ">"); | 
  | 100 |  |          } | 
  | 101 | 805 |          shouldEscape = DEFAULT_ESCAPE; | 
  | 102 | 805 |      } | 
  | 103 |  |   | 
  | 104 |  |      public void writeAttribute(final String name, final Object value, | 
  | 105 |  |              final String property) throws IOException { | 
  | 106 | 1436 |          AssertionUtil.assertNotNull("name", name); | 
  | 107 | 1435 |          if (!startTagOpening) { | 
  | 108 | 1 |              throw new IllegalStateException( | 
  | 109 |  |                      "there is no currently open element"); | 
  | 110 |  |          } | 
  | 111 | 1434 |          final String strValue = (value == null) ? "" : value.toString(); | 
  | 112 | 1434 |          final Writer writer = getWriter(); | 
  | 113 | 1434 |          writer.write(" "); | 
  | 114 | 1434 |          writer.write(name); | 
  | 115 | 1434 |          writer.write("=\""); | 
  | 116 | 1434 |          writer.write(escapeAttribute(strValue)); | 
  | 117 | 1434 |          writer.write("\""); | 
  | 118 | 1434 |      } | 
  | 119 |  |   | 
  | 120 |  |      public void writeURIAttribute(final String name, final Object value, | 
  | 121 |  |              final String property) throws IOException { | 
  | 122 | 42 |          AssertionUtil.assertNotNull("name", name); | 
  | 123 | 41 |          if (!startTagOpening) { | 
  | 124 | 1 |              throw new IllegalStateException( | 
  | 125 |  |                      "there is no currently open element"); | 
  | 126 |  |          } | 
  | 127 | 40 |          final Writer writer = getWriter(); | 
  | 128 | 40 |          final String strValue = value.toString(); | 
  | 129 |  |   | 
  | 130 | 40 |          writer.write(" "); | 
  | 131 | 40 |          writer.write(name); | 
  | 132 | 40 |          writer.write("=\""); | 
  | 133 | 40 |          if (StringUtil.startsWithIgnoreCase(strValue, "javascript:")) { | 
  | 134 | 1 |              writer.write(escapeAttribute(strValue)); | 
  | 135 |  |          } else { | 
  | 136 | 39 |              writer.write(encodeURIAttribute(strValue)); | 
  | 137 |  |          } | 
  | 138 | 40 |          writer.write("\""); | 
  | 139 | 40 |      } | 
  | 140 |  |   | 
  | 141 |  |      public void writeComment(final Object comment) throws IOException { | 
  | 142 | 3 |          AssertionUtil.assertNotNull("comment", comment); | 
  | 143 | 2 |          final Writer writer = getWriter(); | 
  | 144 | 2 |          closeStartTagIfOpening(writer); | 
  | 145 | 2 |          writer.write("<!--"); | 
  | 146 | 2 |          writer.write(comment.toString()); | 
  | 147 | 2 |          writer.write("-->"); | 
  | 148 | 2 |      } | 
  | 149 |  |   | 
  | 150 |  |      public void writeText(final Object text, final String property) | 
  | 151 |  |              throws IOException { | 
  | 152 | 320 |          AssertionUtil.assertNotNull("text", text); | 
  | 153 | 319 |          final Writer writer = getWriter(); | 
  | 154 | 319 |          closeStartTagIfOpening(writer); | 
  | 155 | 319 |          String str = text.toString(); | 
  | 156 | 319 |          if (shouldEscape) { | 
  | 157 | 317 |              str = htmlSpecialChars(str); | 
  | 158 |  |          } | 
  | 159 | 319 |          writer.write(str); | 
  | 160 | 319 |      } | 
  | 161 |  |   | 
  | 162 |  |      public void writeText(final char text[], final int off, final int len) | 
  | 163 |  |              throws IOException { | 
  | 164 | 4 |          AssertionUtil.assertNotNull("text", text); | 
  | 165 | 3 |          writeText(new String(text, off, len), null); | 
  | 166 | 3 |      } | 
  | 167 |  |   | 
  | 168 |  |      protected String htmlSpecialChars(final String s) { | 
  | 169 | 317 |          return HTMLEncodeUtil.encode(s, true, true); | 
  | 170 |  |      } | 
  | 171 |  |   | 
  | 172 |  |      protected String escapeAttribute(final String s) { | 
  | 173 | 1435 |          return HTMLEncodeUtil.encode(s, false, false); | 
  | 174 |  |      } | 
  | 175 |  |   | 
  | 176 |  |      public ResponseWriter cloneWithWriter(final Writer writer) { | 
  | 177 | 7 |          AssertionUtil.assertNotNull("writer", writer); | 
  | 178 | 6 |          final HtmlResponseWriter clone = new HtmlResponseWriter(); | 
  | 179 | 6 |          clone.setWriter(writer); | 
  | 180 | 6 |          clone.setContentType(getContentType()); | 
  | 181 | 6 |          clone.setCharacterEncoding(getCharacterEncoding()); | 
  | 182 | 6 |          return clone; | 
  | 183 |  |      } | 
  | 184 |  |   | 
  | 185 |  |      public void write(final char[] cbuf, final int off, final int len) | 
  | 186 |  |              throws IOException { | 
  | 187 | 3 |          final Writer writer = getWriter(); | 
  | 188 | 3 |          closeStartTagIfOpening(writer); | 
  | 189 | 3 |          writer.write(cbuf, off, len); | 
  | 190 | 3 |      } | 
  | 191 |  |   | 
  | 192 |  |      public void write(final char[] cbuf) throws IOException { | 
  | 193 | 2 |          final Writer writer = getWriter(); | 
  | 194 | 2 |          closeStartTagIfOpening(writer); | 
  | 195 | 2 |          writer.write(cbuf); | 
  | 196 | 2 |      } | 
  | 197 |  |   | 
  | 198 |  |      public void write(final int c) throws IOException { | 
  | 199 | 2 |          final Writer writer = getWriter(); | 
  | 200 | 2 |          closeStartTagIfOpening(writer); | 
  | 201 | 2 |          writer.write(c); | 
  | 202 | 2 |      } | 
  | 203 |  |   | 
  | 204 |  |      public void write(final String str) throws IOException { | 
  | 205 | 94 |          final Writer writer = getWriter(); | 
  | 206 | 94 |          closeStartTagIfOpening(writer); | 
  | 207 | 94 |          writer.write(str); | 
  | 208 | 94 |      } | 
  | 209 |  |   | 
  | 210 |  |      public void write(final String str, final int off, final int len) | 
  | 211 |  |              throws IOException { | 
  | 212 | 2 |          final Writer writer = getWriter(); | 
  | 213 | 2 |          closeStartTagIfOpening(writer); | 
  | 214 | 2 |          writer.write(str, off, len); | 
  | 215 | 2 |      } | 
  | 216 |  |   | 
  | 217 |  |      public void flush() throws IOException { | 
  | 218 | 7 |          final Writer writer = getWriter(); | 
  | 219 | 7 |          closeStartTagIfOpening(writer); | 
  | 220 | 7 |      } | 
  | 221 |  |   | 
  | 222 |  |      public void close() throws IOException { | 
  | 223 | 2 |          final Writer writer = getWriter(); | 
  | 224 | 2 |          closeStartTagIfOpening(writer); | 
  | 225 | 2 |          writer.close(); | 
  | 226 | 2 |      } | 
  | 227 |  |   | 
  | 228 |  |      public void startDocument() throws IOException { | 
  | 229 | 6 |      } | 
  | 230 |  |   | 
  | 231 |  |      public void endDocument() throws IOException { | 
  | 232 | 4 |          closeStartTagIfOpening(getWriter()); | 
  | 233 | 4 |      } | 
  | 234 |  |   | 
  | 235 |  |      public String getContentType() { | 
  | 236 | 11 |          return contentType; | 
  | 237 |  |      } | 
  | 238 |  |   | 
  | 239 |  |      public void setContentType(final String contentType) { | 
  | 240 | 10 |          this.contentType = contentType; | 
  | 241 | 10 |      } | 
  | 242 |  |   | 
  | 243 |  |      public String getCharacterEncoding() { | 
  | 244 | 110 |          if (characterEncoding == null) { | 
  | 245 | 44 |              characterEncoding = JsfConstants.DEFAULT_ENCODING; | 
  | 246 |  |          } | 
  | 247 | 110 |          return characterEncoding; | 
  | 248 |  |      } | 
  | 249 |  |   | 
  | 250 |  |      public void setCharacterEncoding(final String characterEncoding) { | 
  | 251 | 10 |          this.characterEncoding = characterEncoding; | 
  | 252 | 10 |      } | 
  | 253 |  |   | 
  | 254 |  |      public Writer getWriter() { | 
  | 255 | 3943 |          return writer; | 
  | 256 |  |      } | 
  | 257 |  |   | 
  | 258 |  |      public void setWriter(final Writer writer) { | 
  | 259 | 414 |          this.writer = writer; | 
  | 260 | 414 |      } | 
  | 261 |  |   | 
  | 262 |  |      protected String encodeURIAttribute(final String url) throws IOException { | 
  | 263 | 51 |          final char[] chars = url.toCharArray(); | 
  | 264 | 51 |          final StringBuffer sb = new StringBuffer(url.length() + 100); | 
  | 265 | 51 |          final int length = chars.length; | 
  | 266 | 51 |          final String encoding = getCharacterEncoding(); | 
  | 267 | 320 |          for (int i = 0; i < length; i++) { | 
  | 268 | 289 |              final char c = chars[i]; | 
  | 269 | 289 |              if (ArrayUtil.contains(unescape, c)) { | 
  | 270 | 54 |                  sb.append(c); | 
  | 271 | 54 |                  if ('?' == c) { | 
  | 272 | 20 |                      if (i < length) { | 
  | 273 | 20 |                          sb.append(encodeQueryString(url.substring(i + 1))); | 
  | 274 |  |                      } | 
  | 275 |  |                      break; | 
  | 276 |  |                  } | 
  | 277 |  |              } else { | 
  | 278 | 235 |                  sb.append(URLEncoder.encode(String.valueOf(c), encoding)); | 
  | 279 |  |              } | 
  | 280 |  |          } | 
  | 281 | 51 |          return new String(sb); | 
  | 282 |  |      } | 
  | 283 |  |   | 
  | 284 |  |      public String encodeQueryString(final String s) throws IOException { | 
  | 285 | 20 |          final char[] chars = s.toCharArray(); | 
  | 286 | 20 |          final StringBuffer sb = new StringBuffer(s.length() + 32); | 
  | 287 | 20 |          final String encoding = getCharacterEncoding(); | 
  | 288 | 223 |          for (int i = 0; i < chars.length; i++) { | 
  | 289 | 203 |              final char c = chars[i]; | 
  | 290 | 203 |              switch (c) { | 
  | 291 |  |              case ' ':  | 
  | 292 | 1 |                  sb.append("%20"); | 
  | 293 | 1 |                  break; | 
  | 294 |  |              case '!':  | 
  | 295 | 0 |                  sb.append("!"); | 
  | 296 | 0 |                  break; | 
  | 297 |  |              case '#':  | 
  | 298 | 2 |                  sb.append("#"); | 
  | 299 | 2 |                  break; | 
  | 300 |  |              case '%':  | 
  | 301 | 5 |                  sb.append("%"); | 
  | 302 | 5 |                  break; | 
  | 303 |  |              case '&':  | 
  | 304 |  |                   | 
  | 305 | 7 |                  sb.append("&"); | 
  | 306 | 7 |                  break; | 
  | 307 |  |              case '\'':  | 
  | 308 | 0 |                  sb.append("'"); | 
  | 309 | 0 |                  break; | 
  | 310 |  |              case '(':  | 
  | 311 | 0 |                  sb.append("("); | 
  | 312 | 0 |                  break; | 
  | 313 |  |              case ')':  | 
  | 314 | 0 |                  sb.append(")"); | 
  | 315 | 0 |                  break; | 
  | 316 |  |              case '+':  | 
  | 317 | 0 |                  sb.append("+"); | 
  | 318 | 0 |                  break; | 
  | 319 |  |              case '/':  | 
  | 320 | 2 |                  sb.append("/"); | 
  | 321 | 2 |                  break; | 
  | 322 |  |              case '=':  | 
  | 323 | 28 |                  sb.append("="); | 
  | 324 | 28 |                  break; | 
  | 325 |  |              case '~':  | 
  | 326 | 0 |                  sb.append("~"); | 
  | 327 | 0 |                  break; | 
  | 328 |  |              default: | 
  | 329 | 158 |                  sb.append(URLEncoder.encode(String.valueOf(c), encoding)); | 
  | 330 |  |                  break; | 
  | 331 |  |              } | 
  | 332 |  |          } | 
  | 333 | 20 |          return new String(sb); | 
  | 334 |  |      } | 
  | 335 |  |   | 
  | 336 |  |      public String toString() { | 
  | 337 | 1 |          return writer.toString(); | 
  | 338 |  |      } | 
  | 339 |  |   | 
  | 340 |  |  } |