View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.internal.webapp;
21  
22  import org.apache.myfaces.tobago.internal.util.FastStringWriter;
23  import org.apache.myfaces.tobago.internal.util.HtmlWriterUtils;
24  import org.apache.myfaces.tobago.internal.util.JsonWriterUtils;
25  import org.apache.myfaces.tobago.internal.util.StringUtils;
26  import org.apache.myfaces.tobago.internal.util.WriterUtils;
27  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
28  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
29  
30  import javax.faces.context.ResponseWriter;
31  import java.io.IOException;
32  import java.io.Writer;
33  import java.util.Arrays;
34  
35  public class HtmlResponseWriter extends TobagoResponseWriterBase {
36  
37    private static final String HTML_DOCTYPE = "<!DOCTYPE html>";
38  
39    private final WriterUtils helper;
40    private FastStringWriter javascriptWriter;
41    private boolean javascriptMode;
42  
43    public HtmlResponseWriter(
44        final Writer writer, final String contentType, final String characterEncoding) {
45      super(writer, contentType, characterEncoding);
46      if ("application/json".equals(contentType)) {
47        this.helper = new JsonWriterUtils(writer, characterEncoding);
48      } else {
49        this.helper = new HtmlWriterUtils(writer, characterEncoding);
50      }
51      this.javascriptWriter = new FastStringWriter();
52    }
53  
54    /**
55     * @deprecated Should not be used, because it conflicts with CSP.
56     */
57    @Deprecated
58    @Override
59    public void endJavascript() throws IOException {
60      javascriptMode = false;
61    }
62  
63    /**
64     * @deprecated Should not be used, because it conflicts with CSP.
65     */
66    @Deprecated
67    @Override
68    public void startJavascript() throws IOException {
69      javascriptMode = true;
70    }
71  
72    @Override
73    public void write(final String string) throws IOException {
74      if (javascriptMode) {
75        writeJavascript(string);
76      } else {
77        writeInternal(getWriter(), string);
78      }
79    }
80  
81    /**
82     * @deprecated Should not be used, because it conflicts with CSP.
83     */
84    @Deprecated
85    @Override
86    public void writeJavascript(final String script) throws IOException {
87      writeInternal(javascriptWriter, script);
88    }
89  
90    public String getJavascript() {
91      return javascriptWriter.toString();
92    }
93  
94    public final WriterUtils getHelper() {
95      return helper;
96    }
97  
98    @Override
99    public void writeText(final Object text, final String property)
100       throws IOException {
101     closeOpenTag();
102     final String value = findValue(text, property);
103     helper.writeText(value);
104   }
105 
106   @Override
107   public void writeText(final char[] text, final int offset, final int length)
108       throws IOException {
109     closeOpenTag();
110     helper.writeText(text, offset, length);
111   }
112 
113   @Override
114   public void write(final char[] cbuf, final int off, final int len) throws IOException {
115     // Related to http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-696
116     if (Arrays.equals(cbuf, XML_VERSION_1_0_ENCODING_UTF_8_CHARS)) {
117       // drop
118     } else {
119       super.write(cbuf, off, len);
120     }
121   }
122 
123   @Override
124   protected void closeEmptyTag() throws IOException {
125     getWriter().write(">");
126   }
127 
128   @Override
129   protected void writerAttributeValue(final String value, final boolean escape) throws IOException {
130     if (escape) {
131       helper.writeAttributeValue(value);
132     } else {
133       getWriter().write(value);
134     }
135   }
136 
137   @Override
138   public ResponseWriter cloneWithWriter(final Writer originalWriter) {
139     return new HtmlResponseWriter(
140         originalWriter, getContentType(), getCharacterEncoding());
141   }
142 
143   @Override
144   public void startDocument() throws IOException {
145     getWriter().write(HTML_DOCTYPE);
146     getWriter().write('\n');
147     startElement(HtmlElements.HTML);
148   }
149 
150   @Override
151   public void endElement(final String name) throws IOException {
152     if (name.equals(HtmlElements.BODY.getValue())) {
153       final String javascript = getJavascript();
154       if (StringUtils.isNotEmpty(javascript)) {
155         startElement(HtmlElements.SCRIPT);
156         writeAttribute(HtmlAttributes.TYPE, "text/javascript", false);
157         write(javascript);
158         super.endElement(HtmlElements.SCRIPT);
159       }
160     }
161     super.endElement(name);
162   }
163 
164   @Override
165   public void endDocument() throws IOException {
166 
167     endElement(HtmlElements.HTML);
168   }
169 }