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    }