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<>();
42    private Set<MarkupLanguageAttributes> usedAttributes = new HashSet<>();
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    /**
75     * @deprecated since 1.0.11
76     */
77    @Override
78    @Deprecated
79    public void writeAttribute(final String name, final Object value, final String property) throws IOException {
80      responseWriter.writeAttribute(name, value, property);
81    }
82  
83    /**
84     * @deprecated since 1.0.11
85     */
86    @Override
87    @Deprecated
88    public void writeText(final Object text, final String property) throws IOException {
89      responseWriter.writeText(text, property);
90    }
91  
92    @Override
93    public void flush() throws IOException {
94      responseWriter.flush();
95    }
96  
97    @Override
98    public void writeAttribute(final MarkupLanguageAttributes name, final String value, final boolean escape)
99        throws IOException {
100     responseWriter.writeAttribute(name, value, escape);
101     if (usedAttributes.contains(name)) {
102       LOG.error("Duplicate attribute '" + name + "' in element <" + stack.peek() + "> with value '" + value + "'!",
103           new IllegalStateException());
104     } else {
105       usedAttributes.add(name);
106     }
107   }
108 
109   @Override
110   public void writeAttribute(final MarkupLanguageAttributes name, final HtmlTypes types) throws IOException {
111     responseWriter.writeAttribute(name, types);
112     if (usedAttributes.contains(name)) {
113       LOG.error("Duplicate attribute '" + name + "' in element <" + stack.peek() + "> with value '" + types + "'!",
114           new IllegalStateException());
115       usedAttributes.add(name);
116     }
117   }
118 
119   @Override
120   public void writeURIAttribute(final MarkupLanguageAttributes name, final String string) throws IOException {
121     responseWriter.writeURIAttribute(name, string);
122     if (usedAttributes.contains(name)) {
123       LOG.error("Duplicate attribute '" + name + "' in element <" + stack.peek() + "> with value '" + string + "'!",
124           new IllegalStateException());
125     } else {
126       usedAttributes.add(name);
127     }
128   }
129 
130   @Override
131   public String getContentType() {
132     return responseWriter.getContentType();
133   }
134 
135   @Override
136   public String getCharacterEncoding() {
137     return responseWriter.getCharacterEncoding();
138   }
139 
140   @Override
141   public void startDocument() throws IOException {
142     responseWriter.startDocument();
143   }
144 
145   @Override
146   public void endDocument() throws IOException {
147     responseWriter.endDocument();
148   }
149 
150   @Override
151   public void writeURIAttribute(final String name, final Object value, final String property) throws IOException {
152     responseWriter.writeURIAttribute(name, value, property);
153   }
154 
155   @Override
156   public void writeText(final char[] text, final int off, final int len) throws IOException {
157     responseWriter.writeText(text, off, len);
158   }
159 
160   @Override
161   public void write(final char[] chars, final int i, final int i1) throws IOException {
162     responseWriter.write(chars, i, i1);
163   }
164 
165   @Override
166   public void close() throws IOException {
167     responseWriter.close();
168   }
169 
170   @Override
171   public void startElement(final String name, final UIComponent currentComponent) throws IOException {
172     if (LOG.isDebugEnabled()) {
173       LOG.debug("start element: '" + name + "'");
174     }
175     stack.push(name);
176     responseWriter.startElement(name, currentComponent);
177 
178     usedAttributes.clear();
179   }
180 
181   @Override
182   public void startElement(final HtmlElements name) throws IOException {
183     if (LOG.isDebugEnabled()) {
184       LOG.debug("start element: '" + name + "'");
185     }
186     stack.push(name);
187     responseWriter.startElement(name);
188 
189     usedAttributes.clear();
190   }
191 
192   @Override
193   public void endElement(final String name) throws IOException {
194     if (LOG.isDebugEnabled()) {
195       LOG.debug("end element: '" + name + "'");
196     }
197     Object top;
198     try {
199       top = stack.pop();
200     } catch (final EmptyStackException e) {
201       LOG.error("Failed to close element \"" + name + "\"!", e);
202       top = "*** failure ***";
203     }
204 
205     if (!top.equals(name)) {
206       LOG.error("Element end with name='" + name + "' doesn't match with top element on the stack='" + top + "'.",
207           new IllegalArgumentException());
208     }
209     responseWriter.endElement(name);
210   }
211 
212   @Override
213   public void endElement(final HtmlElements name) throws IOException {
214     if (LOG.isDebugEnabled()) {
215       LOG.debug("end element: '" + name + "'");
216     }
217     Object top;
218     try {
219       top = stack.pop();
220     } catch (final EmptyStackException e) {
221       LOG.error("Failed to close element \"" + name + "\"!", e);
222       top = "*** failure ***";
223     }
224 
225     if (!top.equals(name)) {
226       LOG.error("Element end with name='" + name + "' doesn't match with top element on the stack='" + top + "'.",
227           new IllegalArgumentException());
228     }
229     responseWriter.endElement(name);
230   }
231 }