001 // Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.internal.services;
016
017 import org.apache.tapestry5.MarkupWriter;
018 import org.apache.tapestry5.MarkupWriterListener;
019 import org.apache.tapestry5.dom.*;
020 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
021 import org.apache.tapestry5.ioc.internal.util.Defense;
022
023 import java.io.PrintWriter;
024 import java.util.Collection;
025 import java.util.List;
026
027 public class MarkupWriterImpl implements MarkupWriter
028 {
029 private final Document document;
030
031 private Element current;
032
033 private Text currentText;
034
035 private List<MarkupWriterListener> listeners;
036
037 /**
038 * Creates a new instance of the MarkupWriter with a {@link org.apache.tapestry5.dom.DefaultMarkupModel}.
039 */
040 public MarkupWriterImpl()
041 {
042 this(new DefaultMarkupModel());
043 }
044
045 public MarkupWriterImpl(MarkupModel model)
046 {
047 this(model, null);
048 }
049
050 public MarkupWriterImpl(MarkupModel model, String encoding)
051 {
052 document = new Document(model, encoding);
053 }
054
055 public void toMarkup(PrintWriter writer)
056 {
057 document.toMarkup(writer);
058 }
059
060 @Override
061 public String toString()
062 {
063 return document.toString();
064 }
065
066 public Document getDocument()
067 {
068 return document;
069 }
070
071 public Element getElement()
072 {
073 return current;
074 }
075
076 public void cdata(String content)
077 {
078 currentText = null;
079
080 if (current == null)
081 {
082 document.cdata(content);
083 }
084 else
085 {
086 current.cdata(content);
087 }
088 }
089
090 public void write(String text)
091 {
092 if (text == null) return;
093
094 if (currentText == null)
095 {
096 currentText =
097 current == null
098 ? document.text(text)
099 : current.text(text);
100
101 return;
102 }
103
104 currentText.write(text);
105 }
106
107 public void writef(String format, Object... args)
108 {
109 // A bit of a cheat:
110
111 write("");
112 currentText.writef(format, args);
113 }
114
115 public void attributes(Object... namesAndValues)
116 {
117 ensureCurrentElement();
118
119 int i = 0;
120
121 while (i < namesAndValues.length)
122 {
123 // name should never be null.
124
125 String name = namesAndValues[i++].toString();
126 Object value = namesAndValues[i++];
127
128 if (value == null) continue;
129
130 current.attribute(name, value.toString());
131 }
132 }
133
134 private void ensureCurrentElement()
135 {
136 if (current == null)
137 throw new IllegalStateException(ServicesMessages.markupWriterNoCurrentElement());
138 }
139
140 public Element element(String name, Object... namesAndValues)
141 {
142 if (current == null)
143 {
144 Element existingRootElement = document.getRootElement();
145
146 if (existingRootElement != null)
147 throw new IllegalStateException(String.format(
148 "A document must have exactly one root element. Element <%s> is already the root element.",
149 existingRootElement.getName()));
150
151 current = document.newRootElement(name);
152 }
153 else
154 {
155 current = current.element(name);
156 }
157
158 attributes(namesAndValues);
159
160 currentText = null;
161
162 fireElementDidStart();
163
164 return current;
165 }
166
167 public void writeRaw(String text)
168 {
169 currentText = null;
170
171 if (current == null)
172 {
173 document.raw(text);
174 }
175 else
176 {
177 current.raw(text);
178 }
179 }
180
181 public Element end()
182 {
183 ensureCurrentElement();
184
185 fireElementDidEnd();
186
187 current = current.getParent();
188
189 currentText = null;
190
191 return current;
192 }
193
194 public void comment(String text)
195 {
196 currentText = null;
197
198 if (current == null)
199 {
200 document.comment(text);
201 }
202 else
203 {
204 current.comment(text);
205 }
206 }
207
208 public Element attributeNS(String namespace, String attributeName, String attributeValue)
209 {
210 ensureCurrentElement();
211
212 current.attribute(namespace, attributeName, attributeValue);
213
214 return current;
215 }
216
217 public Element defineNamespace(String namespace, String namespacePrefix)
218 {
219 ensureCurrentElement();
220
221 current.defineNamespace(namespace, namespacePrefix);
222
223 return current;
224 }
225
226 public Element elementNS(String namespace, String elementName)
227 {
228 if (current == null) current = document.newRootElement(namespace, elementName);
229 else current = current.elementNS(namespace, elementName);
230
231 currentText = null;
232
233 fireElementDidStart();
234
235 return current;
236 }
237
238 public void addListener(MarkupWriterListener listener)
239 {
240 Defense.notNull(listener, "listener");
241
242 if (listeners == null) listeners = CollectionFactory.newList();
243
244 listeners.add(listener);
245 }
246
247 public void removeListener(MarkupWriterListener listener)
248 {
249 if (listeners != null)
250 listeners.remove(listener);
251 }
252
253 private void fireElementDidStart()
254 {
255 if (isEmpty(listeners)) return;
256
257 for (MarkupWriterListener l : CollectionFactory.newList(listeners))
258 {
259 l.elementDidStart(current);
260 }
261 }
262
263 private static boolean isEmpty(Collection<?> collection)
264 {
265 return collection == null || collection.isEmpty();
266 }
267
268 private void fireElementDidEnd()
269 {
270 if (isEmpty(listeners)) return;
271
272 for (MarkupWriterListener l : CollectionFactory.newList(listeners))
273 {
274 l.elementDidEnd(current);
275 }
276 }
277 }
278