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.StringUtils;
23  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
24  import org.apache.myfaces.tobago.renderkit.html.HtmlTypes;
25  import org.apache.myfaces.tobago.renderkit.html.MarkupLanguageAttributes;
26  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  import javax.faces.component.UIComponent;
31  import javax.faces.context.ResponseWriter;
32  import java.io.IOException;
33  import java.io.Writer;
34  import java.util.EmptyStackException;
35  import java.util.HashSet;
36  import java.util.Set;
37  import java.util.Stack;
38  
39  public class DebugResponseWriterWrapper extends TobagoResponseWriter {
40  
41    private Stack<Object> stack = new Stack<Object>();
42    private Set<MarkupLanguageAttributes> usedAttributes = new HashSet<MarkupLanguageAttributes>();
43  
44    private static final Logger LOG = LoggerFactory.getLogger(DebugResponseWriterWrapper.class);
45  
46    private final TobagoResponseWriter responseWriter;
47  
48    public DebugResponseWriterWrapper(final TobagoResponseWriter responseWriter) {
49      this.responseWriter = responseWriter;
50    }
51  
52    @Override
53    public void write(final String string) throws IOException {
54      responseWriter.write(string);
55    }
56  
57    @Override
58    public void writeComment(final Object comment) throws IOException {
59      String commentStr = comment.toString();
60      if (commentStr.indexOf("--") > 0) {
61        LOG.error("Comment must not contain the sequence '--', comment = '" + comment + "'.",
62            new IllegalArgumentException());
63  
64        commentStr = StringUtils.replace(commentStr, "--", "++");
65      }
66      responseWriter.writeComment(commentStr);
67    }
68  
69    @Override
70    public ResponseWriter cloneWithWriter(final Writer writer) {
71      return new DebugResponseWriterWrapper((TobagoResponseWriter) responseWriter.cloneWithWriter(writer));
72    }
73  
74    @Override
75    @Deprecated
76    public void writeAttribute(final String name, final Object value, final String property) throws IOException {
77      responseWriter.writeAttribute(name, value, property);
78    }
79  
80    @Override
81    @Deprecated
82    public void writeText(final Object text, final String property) throws IOException {
83      responseWriter.writeText(text, property);
84    }
85  
86    @Override
87    public void flush() throws IOException {
88      responseWriter.flush();
89    }
90  
91    @Override
92    public void writeAttribute(final MarkupLanguageAttributes name, final String value, final boolean escape)
93        throws IOException {
94      responseWriter.writeAttribute(name, value, escape);
95      if (usedAttributes.contains(name)) {
96        LOG.error("Duplicate attribute '" + name + "' in element <" + stack.peek() + ">!", new IllegalStateException());
97      } else {
98        usedAttributes.add(name);
99      }
100   }
101 
102   @Override
103   public void writeAttribute(final MarkupLanguageAttributes name, final HtmlTypes types) throws IOException {
104     responseWriter.writeAttribute(name, types);
105     if (usedAttributes.contains(name)) {
106       LOG.error("Duplicate attribute '" + name + "' in element <" + stack.peek() + ">!", new IllegalStateException());
107     } else {
108       usedAttributes.add(name);
109     }
110   }
111 
112   @Override
113   public void writeURIAttribute(MarkupLanguageAttributes name, String string) throws IOException {
114     responseWriter.writeURIAttribute(name, string);
115     if (usedAttributes.contains(name)) {
116       LOG.error("Duplicate attribute '" + name + "' in element <" + stack.peek() + ">!", new IllegalStateException());
117     } else {
118       usedAttributes.add(name);
119     }
120   }
121 
122   @Override
123   public String getContentType() {
124     return responseWriter.getContentType();
125   }
126 
127   @Override
128   public String getCharacterEncoding() {
129     return responseWriter.getCharacterEncoding();
130   }
131 
132   @Override
133   public void startDocument() throws IOException {
134     responseWriter.startDocument();
135   }
136 
137   @Override
138   public void endDocument() throws IOException {
139     responseWriter.endDocument();
140   }
141 
142   /**
143    * @deprecated Should not be used, because it conflicts with CSP.
144    */
145   @Deprecated
146   @Override
147   public void writeJavascript(final String script) throws IOException {
148     responseWriter.writeJavascript(script);
149   }
150 
151   /**
152    * @deprecated Should not be used, because it conflicts with CSP.
153    */
154   @Deprecated
155   @Override
156   public void endJavascript() throws IOException {
157     responseWriter.endJavascript();
158   }
159 
160   /**
161    * @deprecated Should not be used, because it conflicts with CSP.
162    */
163   @Deprecated
164   @Override
165   public void startJavascript() throws IOException {
166     responseWriter.startJavascript();
167   }
168 
169   @Override
170   public void writeURIAttribute(final String name, final Object value, final String property) throws IOException {
171     responseWriter.writeURIAttribute(name, value, property);
172   }
173 
174   @Override
175   public void writeText(final char[] text, final int off, final int len) throws IOException {
176     responseWriter.writeText(text, off, len);
177   }
178 
179   @Override
180   public void write(final char[] chars, final int i, final int i1) throws IOException {
181     responseWriter.write(chars, i, i1);
182   }
183 
184   @Override
185   public void close() throws IOException {
186     responseWriter.close();
187   }
188 
189   @Override
190   public void startElement(final String name, final UIComponent currentComponent) throws IOException {
191     if (LOG.isDebugEnabled()) {
192       LOG.debug("start element: '" + name + "'");
193     }
194     stack.push(name);
195     responseWriter.startElement(name, currentComponent);
196 
197     usedAttributes.clear();
198   }
199 
200   @Override
201   public void startElement(final HtmlElements name) throws IOException {
202     if (LOG.isDebugEnabled()) {
203       LOG.debug("start element: '" + name + "'");
204     }
205     stack.push(name);
206     responseWriter.startElement(name);
207 
208     usedAttributes.clear();
209   }
210 
211   @Override
212   public void endElement(final String name) throws IOException {
213     if (LOG.isDebugEnabled()) {
214       LOG.debug("end element: '" + name + "'");
215     }
216     Object top;
217     try {
218       top = stack.pop();
219     } catch (final EmptyStackException e) {
220       LOG.error("Failed to close element \"" + name + "\"!", e);
221       top = "*** failure ***";
222     }
223 
224     if (!top.equals(name)) {
225       LOG.error("Element end with name='" + name + "' doesn't match with top element on the stack='" + top + "'.",
226           new IllegalArgumentException());
227     }
228     responseWriter.endElement(name);
229   }
230 
231   @Override
232   public void endElement(final HtmlElements name) throws IOException {
233     if (LOG.isDebugEnabled()) {
234       LOG.debug("end element: '" + name + "'");
235     }
236     Object top;
237     try {
238       top = stack.pop();
239     } catch (final EmptyStackException e) {
240       LOG.error("Failed to close element \"" + name + "\"!", e);
241       top = "*** failure ***";
242     }
243 
244     if (!top.equals(name)) {
245       LOG.error("Element end with name='" + name + "' doesn't match with top element on the stack='" + top + "'.",
246           new IllegalArgumentException());
247     }
248     responseWriter.endElement(name);
249   }
250 }