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 }