001 /*
002 * Copyright (c) 2005, 2006, 2007 Henri Sivonen
003 * Copyright (c) 2007 Mozilla Foundation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a
006 * copy of this software and associated documentation files (the "Software"),
007 * to deal in the Software without restriction, including without limitation
008 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
009 * and/or sell copies of the Software, and to permit persons to whom the
010 * Software is furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
018 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
020 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
021 * DEALINGS IN THE SOFTWARE.
022 */
023
024 package nu.validator.messages;
025
026 import nu.validator.messages.types.MessageType;
027 import nu.validator.source.SourceHandler;
028 import nu.validator.xml.AttributesImpl;
029 import nu.validator.xml.XhtmlSaxEmitter;
030
031 import org.xml.sax.ContentHandler;
032 import org.xml.sax.SAXException;
033
034 /**
035 * @version $Id: XhtmlMessageEmitter.java 54 2007-09-20 15:37:38Z hsivonen $
036 * @author hsivonen
037 */
038 public class XhtmlMessageEmitter extends MessageEmitter {
039
040 private static final char[] COLON_SPACE = { ':', ' ' };
041
042 private static final char[] PERIOD = { '.' };
043
044 private static final char[] ON_LINE = "On line ".toCharArray();
045
046 private static final char[] AT_LINE = "At line ".toCharArray();
047
048 private static final char[] FROM_LINE = "From line ".toCharArray();
049
050 private static final char[] TO_LINE = "; to line ".toCharArray();
051
052 private static final char[] COLUMN = ", column ".toCharArray();
053
054 private static final char[] IN_RESOURCE = " in resource ".toCharArray();
055
056 private final AttributesImpl attrs = new AttributesImpl();
057
058 private boolean listOpen = false;
059
060 private final ContentHandler contentHandler;
061
062 private final XhtmlSaxEmitter emitter;
063
064 private final XhtmlMessageTextHandler messageTextHandler;
065
066 private final XhtmlExtractHandler extractHandler;
067
068 private boolean textEmitted;
069
070 private String systemId;
071
072 private int oneBasedFirstLine;
073
074 private int oneBasedFirstColumn;
075
076 private int oneBasedLastLine;
077
078 private int oneBasedLastColumn;
079
080 private boolean exact;
081
082 private boolean willShowSource;
083
084 /**
085 * @param contentHandler
086 */
087 public XhtmlMessageEmitter(ContentHandler contentHandler) {
088 super();
089 this.contentHandler = contentHandler;
090 this.emitter = new XhtmlSaxEmitter(contentHandler);
091 this.messageTextHandler = new XhtmlMessageTextHandler(emitter);
092 this.extractHandler = new XhtmlExtractHandler(emitter);
093 }
094
095 private void maybeOpenList() throws SAXException {
096 if (!this.listOpen) {
097 this.emitter.startElement("ol");
098 this.listOpen = true;
099 }
100 }
101
102 private void emitErrorLevel(char[] level) throws SAXException {
103 this.emitter.startElement("strong");
104 this.emitter.characters(level);
105 this.emitter.endElement("strong");
106 }
107
108 @Override
109 public void endMessage() throws SAXException {
110 maybeCloseTextPara();
111 this.emitter.endElement("li");
112 }
113
114 private void maybeCloseTextPara() throws SAXException {
115 if (!textEmitted) {
116 this.emitter.characters(PERIOD);
117 this.emitter.endElement("p");
118 maybeEmitLocation();
119 }
120 }
121
122 private void maybeEmitLocation() throws SAXException {
123 if (oneBasedLastLine == -1 && systemId == null) {
124 return;
125 }
126 this.emitter.startElementWithClass("p", "location");
127 if (oneBasedLastLine == -1) {
128 emitSystemId();
129 } else if (oneBasedLastColumn == -1) {
130 emitLineLocation();
131 } else if (oneBasedFirstLine == -1
132 || (oneBasedFirstLine == oneBasedLastLine && oneBasedFirstColumn == oneBasedLastColumn)) {
133 emitSingleLocation();
134 } else {
135 emitRangeLocation();
136 }
137 this.emitter.endElement("p");
138 }
139
140 /**
141 * @throws SAXException
142 */
143 private void maybeEmitInResource() throws SAXException {
144 if (systemId != null) {
145 this.emitter.characters(IN_RESOURCE);
146 emitSystemId();
147 }
148 }
149
150 /**
151 * @throws SAXException
152 */
153 private void emitSystemId() throws SAXException {
154 this.emitter.startElementWithClass("span", "url");
155 this.emitter.characters(systemId);
156 this.emitter.endElement("span");
157 }
158
159 private void emitRangeLocation() throws SAXException {
160 if (willShowSource && systemId == null) {
161 attrs.clear();
162 attrs.addAttribute("href", "#l" + oneBasedLastLine + "c" + oneBasedLastColumn);
163 emitter.startElement("a", attrs);
164 }
165 this.emitter.characters(FROM_LINE);
166 this.emitter.startElementWithClass("span", "first-line");
167 this.emitter.characters(Integer.toString(oneBasedFirstLine));
168 this.emitter.endElement("span");
169 this.emitter.characters(COLUMN);
170 this.emitter.startElementWithClass("span", "first-col");
171 this.emitter.characters(Integer.toString(oneBasedFirstColumn));
172 this.emitter.endElement("span");
173 this.emitter.characters(TO_LINE);
174 this.emitter.startElementWithClass("span", "last-line");
175 this.emitter.characters(Integer.toString(oneBasedLastLine));
176 this.emitter.endElement("span");
177 this.emitter.characters(COLUMN);
178 this.emitter.startElementWithClass("span", "last-col");
179 this.emitter.characters(Integer.toString(oneBasedLastColumn));
180 this.emitter.endElement("span");
181 maybeEmitInResource();
182 if (willShowSource && systemId == null) {
183 emitter.endElement("a");
184 }
185 }
186
187 private void emitSingleLocation() throws SAXException {
188 if (willShowSource && systemId == null) {
189 attrs.clear();
190 attrs.addAttribute("href", "#cl" + oneBasedLastLine + "c" + oneBasedLastColumn);
191 emitter.startElement("a", attrs);
192 }
193 this.emitter.characters(AT_LINE);
194 this.emitter.startElementWithClass("span", "last-line");
195 this.emitter.characters(Integer.toString(oneBasedLastLine));
196 this.emitter.endElement("span");
197 this.emitter.characters(COLUMN);
198 this.emitter.startElementWithClass("span", "last-col");
199 this.emitter.characters(Integer.toString(oneBasedLastColumn));
200 this.emitter.endElement("span");
201 maybeEmitInResource();
202 if (willShowSource && systemId == null) {
203 emitter.endElement("a");
204 }
205 }
206
207 private void emitLineLocation() throws SAXException {
208 if (willShowSource && systemId == null) {
209 attrs.clear();
210 attrs.addAttribute("href", "#l" + oneBasedLastLine);
211 emitter.startElement("a", attrs);
212 }
213 this.emitter.characters(ON_LINE);
214 this.emitter.startElementWithClass("span", "last-line");
215 this.emitter.characters(Integer.toString(oneBasedLastLine));
216 this.emitter.endElement("span");
217 maybeEmitInResource();
218 if (willShowSource && systemId == null) {
219 emitter.endElement("a");
220 }
221 }
222
223 @Override
224 public void startMessage(MessageType type, @SuppressWarnings("hiding")
225 String systemId,
226 @SuppressWarnings("hiding")
227 int oneBasedFirstLine, @SuppressWarnings("hiding")
228 int oneBasedFirstColumn,
229 @SuppressWarnings("hiding")
230 int oneBasedLastLine, @SuppressWarnings("hiding")
231 int oneBasedLastColumn, @SuppressWarnings("hiding")
232 boolean exact)
233 throws SAXException {
234 this.systemId = systemId;
235 this.oneBasedFirstLine = oneBasedFirstLine;
236 this.oneBasedFirstColumn = oneBasedFirstColumn;
237 this.oneBasedLastLine = oneBasedLastLine;
238 this.oneBasedLastColumn = oneBasedLastColumn;
239 this.exact = exact;
240
241 this.maybeOpenList();
242 this.emitter.startElementWithClass("li", type.getFlatType());
243 this.emitter.startElement("p");
244 emitErrorLevel(type.getPresentationName());
245 this.textEmitted = false;
246 }
247
248 /**
249 * @see nu.validator.messages.MessageEmitter#endMessages()
250 */
251 @Override
252 public void endMessages() throws SAXException {
253 maybeCloseList();
254 }
255
256 /**
257 * @throws SAXException
258 */
259 private void maybeCloseList() throws SAXException {
260 if (this.listOpen) {
261 this.emitter.endElement("ol");
262 this.listOpen = false;
263 }
264 }
265
266 /**
267 * @see nu.validator.messages.MessageEmitter#endText()
268 */
269 @Override
270 public void endText() throws SAXException {
271 this.emitter.endElement("span");
272 this.emitter.endElement("p");
273 this.textEmitted = true;
274 maybeEmitLocation();
275 }
276
277 /**
278 * @see nu.validator.messages.MessageEmitter#startMessages(java.lang.String)
279 */
280 @Override
281 public void startMessages(String documentUri, @SuppressWarnings("hiding")
282 boolean willShowSource) throws SAXException {
283 this.willShowSource = willShowSource;
284 }
285
286 /**
287 * @see nu.validator.messages.MessageEmitter#startText()
288 */
289 @Override
290 public MessageTextHandler startText() throws SAXException {
291 this.emitter.characters(COLON_SPACE);
292 this.emitter.startElement("span");
293 return messageTextHandler;
294 }
295
296 /**
297 * @see nu.validator.messages.MessageEmitter#endSource()
298 */
299 @Override
300 public void endSource() throws SAXException {
301 emitter.endElement("code");
302 emitter.endElement("p");
303 }
304
305 /**
306 * @throws SAXException
307 * @see nu.validator.messages.MessageEmitter#startSource()
308 */
309 @Override
310 public SourceHandler startSource() throws SAXException {
311 maybeCloseTextPara();
312 emitter.startElement("p");
313 emitter.startElement("code");
314 return extractHandler;
315 }
316
317 /**
318 * @see nu.validator.messages.MessageEmitter#endFullSource()
319 */
320 @Override
321 public void endFullSource() throws SAXException {
322 }
323
324 /**
325 * @see nu.validator.messages.MessageEmitter#startFullSource()
326 */
327 @Override
328 public SourceHandler startFullSource() throws SAXException {
329 maybeCloseList();
330 return new XhtmlSourceHandler(emitter);
331 }
332
333 /**
334 * @see nu.validator.messages.MessageEmitter#endResult()
335 */
336 @Override
337 public void endResult() throws SAXException {
338 }
339
340 /**
341 * @see nu.validator.messages.MessageEmitter#startResult()
342 */
343 @Override
344 public ResultHandler startResult() throws SAXException {
345 maybeCloseList();
346 return new XhtmlResultHandler(emitter);
347 }
348
349 }