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