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 java.io.IOException;
32  import java.io.Writer;
33  import java.net.URI;
34  
35  public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
36  
37    private static final Logger LOG = LoggerFactory.getLogger(TobagoResponseWriterBase.class);
38  
39    protected static final String XML_VERSION_1_0_ENCODING_UTF_8 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
40  
41    protected static final char[] XML_VERSION_1_0_ENCODING_UTF_8_CHARS = XML_VERSION_1_0_ENCODING_UTF_8.toCharArray();
42  
43    private int i = 0;
44  
45    private UIComponent component;
46  
47    private boolean startStillOpen;
48  
49    private final Writer writer;
50  
51    private final String contentType;
52  
53    private final String characterEncoding;
54  
55    protected TobagoResponseWriterBase(final Writer writer, final String contentType, final String characterEncoding) {
56      this.writer = writer;
57      this.contentType = contentType;
58      this.characterEncoding = characterEncoding != null ? characterEncoding : "UTF-8";
59    }
60  
61    protected final Writer getWriter() {
62      return writer;
63    }
64  
65    protected final UIComponent getComponent() {
66      return component;
67    }
68  
69    protected final void setComponent(final UIComponent component) {
70      this.component = component;
71    }
72  
73    protected final boolean isStartStillOpen() {
74      return startStillOpen;
75    }
76  
77    protected final void setStartStillOpen(final boolean startStillOpen) {
78      this.startStillOpen = startStillOpen;
79    }
80  
81    protected final String findValue(final Object value, final String property) {
82      if (value != null) {
83        return value instanceof String ? (String) value : value.toString();
84      } else if (property != null) {
85        if (component != null) {
86          final Object object = component.getAttributes().get(property);
87          if (object != null) {
88            return object instanceof String ? (String) object : object.toString();
89          } else {
90            return null;
91          }
92        } else {
93          final String trace = getCallingClassStackTraceElementString();
94          LOG.error("Don't know what to do! "
95              + "Property defined, but no component to get a value. "
96              + trace.substring(trace.indexOf('(')));
97          LOG.error("value = 'null'");
98          LOG.error("property = '" + property + "'");
99          return null;
100       }
101     } else {
102       final String trace = getCallingClassStackTraceElementString();
103       LOG.error("Don't know what to do! "
104           + "No value and no property defined. "
105           + trace.substring(trace.indexOf('(')));
106       LOG.error("value = 'null'");
107       LOG.error("property = 'null'");
108       return null;
109     }
110   }
111 
112   @Override
113   public void write(final char[] cbuf, final int off, final int len)
114       throws IOException {
115     writer.write(cbuf, off, len);
116   }
117 
118   @Override
119   public void write(final String string) throws IOException {
120     writeInternal(writer, string);
121   }
122 
123   protected final void writeInternal(final Writer writer, final String string) throws IOException {
124     closeOpenTag();
125     writer.write(string);
126   }
127 
128   @Override
129   public void write(final int i) throws IOException {
130     closeOpenTag();
131     writer.write(i);
132   }
133 
134   @Override
135   public void write(final char[] chars) throws IOException {
136     closeOpenTag();
137     writer.write(chars);
138   }
139 
140   @Override
141   public void write(final String string, final int i, final int i1) throws IOException {
142     closeOpenTag();
143     writer.write(string, i, i1);
144   }
145 
146   @Override
147   public void close() throws IOException {
148     closeOpenTag();
149     writer.close();
150   }
151 
152   @Override
153   public void flush() throws IOException {
154     /*
155     From the api:
156     Flush any ouput buffered by the output method to the underlying Writer or OutputStream.
157     This method will not flush the underlying Writer or OutputStream;
158     it simply clears any values buffered by this ResponseWriter.
159      */
160     closeOpenTag();
161   }
162 
163   protected void closeOpenTag() throws IOException {
164     if (startStillOpen) {
165       writer.write(">");
166       startStillOpen = false;
167     }
168   }
169 
170   @Override
171   public void startDocument() throws IOException {
172     // nothing to do
173   }
174 
175   @Override
176   public void endDocument() throws IOException {
177     // nothing to do
178   }
179 
180   @Override
181   public String getContentType() {
182     return contentType;
183   }
184 
185   @Override
186   public String getCharacterEncoding() {
187     return characterEncoding;
188   }
189 
190   @Override
191   public void startElement(final String name, final UIComponent currentComponent) throws IOException {
192     startElementInternal(writer, name, currentComponent);
193   }
194 
195   @Override
196   public void startElement(final HtmlElements name) throws IOException {
197     startElementInternal(writer, name.getValue(), null);
198     if (!name.isVoid()) {
199       i++;
200     }
201   }
202 
203   protected void startElementInternal(final Writer writer, final String name, final UIComponent currentComponent)
204       throws IOException {
205     this.component = currentComponent;
206 //    closeOpenTag();
207     if (startStillOpen) {
208       writer.write(">");
209     }
210     writer.write("\n" + StringUtils.repeat("  ", i) + "<");
211     writer.write(name);
212     startStillOpen = true;
213   }
214 
215   @Override
216   public void endElement(final String name) throws IOException {
217     if (HtmlElements.isVoid(name)) {
218       closeEmptyTag();
219     } else {
220       endElementInternal(writer, name, false);
221     }
222     startStillOpen = false;
223   }
224 
225   @Override
226   public void endElement(final HtmlElements name) throws IOException {
227     if (name.isVoid()) {
228       closeEmptyTag();
229     } else {
230       if (!name.isVoid()) {
231         i--;
232       }
233       endElementInternal(writer, name.getValue(), name.isInline());
234     }
235     startStillOpen = false;
236   }
237 
238   @Override
239   public void writeComment(final Object obj) throws IOException {
240     closeOpenTag();
241     final String comment = obj.toString();
242     write("\n" + StringUtils.repeat("  ", i) + "<!--");
243     write(comment);
244     write("-->");
245   }
246 
247   @Override
248   @Deprecated
249   public void writeAttribute(final String name, final Object value, final String property)
250       throws IOException {
251 
252     final String attribute = findValue(value, property);
253     writeAttribute(new MarkupLanguageAttributes() {
254       @Override
255       public String getValue() {
256           return name;
257       }
258     }, attribute, true);
259   }
260 
261   protected final String getCallingClassStackTraceElementString() {
262     final StackTraceElement[] stackTrace = new Exception().getStackTrace();
263     int i = 1;
264     while (stackTrace[i].getClassName().contains("TobagoResponseWriter")) {
265       i++;
266     }
267     return stackTrace[i].toString();
268   }
269 
270   @Override
271   public void writeURIAttribute(final String name, final Object value, final String property)
272       throws IOException {
273     if (value != null) {
274       final URI uri = URI.create(value.toString());
275       writeAttribute(name, uri.toASCIIString(), property);
276     }
277   }
278 
279 // interface TobagoResponseWriter //////////////////////////////////////////////////////////////////////////////////
280 
281   @Override
282   public void writeAttribute(final MarkupLanguageAttributes name, final String value, final boolean escape)
283       throws IOException {
284     writeAttributeInternal(writer, name, value, escape);
285   }
286 
287   @Override
288   public void writeAttribute(final MarkupLanguageAttributes name, final HtmlTypes types) throws IOException {
289     writeAttributeInternal(writer, name, types.getValue(), false);
290   }
291 
292   @Override
293   public void writeURIAttribute(final MarkupLanguageAttributes name, final String value)
294       throws IOException {
295     if (value != null) {
296       final URI uri = URI.create(value);
297       writeAttribute(name, uri.toASCIIString(), true);
298     }
299   }
300 
301   protected void endElementInternal(final Writer writer, final String name, final boolean inline) throws IOException {
302     if (startStillOpen) {
303       writer.write(">");
304     }
305     if (inline) {
306       writer.write("</");
307     } else {
308       writer.write("\n" + StringUtils.repeat("  ", i) + "</");
309     }
310     writer.write(name);
311     writer.write(">");
312   }
313 
314   protected abstract void closeEmptyTag() throws IOException;
315 
316   protected void writeAttributeInternal(
317       final Writer writer, final MarkupLanguageAttributes name, final String value, final boolean escape)
318       throws IOException {
319     if (!startStillOpen) {
320       final String trace = getCallingClassStackTraceElementString();
321       final String error = "Cannot write attribute when start-tag not open. "
322           + "name = '" + name + "' "
323           + "value = '" + value + "' "
324           + trace.substring(trace.indexOf('('));
325       LOG.error(error);
326       throw new IllegalStateException(error);
327     }
328 
329     if (value != null) {
330       writer.write(' ');
331       writer.write(name.getValue());
332       writer.write("='");
333       writerAttributeValue(value, escape);
334       writer.write('\'');
335     }
336   }
337   protected abstract void writerAttributeValue(String value, boolean escape) throws IOException;
338 
339 
340 }
341