1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
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 }