001 /*
002 * Copyright (c) 2006 Henri Sivonen
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a
005 * copy of this software and associated documentation files (the "Software"),
006 * to deal in the Software without restriction, including without limitation
007 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
008 * and/or sell copies of the Software, and to permit persons to whom the
009 * Software is furnished to do so, subject to the following conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in
012 * all copies or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
017 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020 * DEALINGS IN THE SOFTWARE.
021 */
022
023 package org.whattf.checker;
024
025 import java.util.LinkedList;
026
027 import org.relaxng.datatype.DatatypeException;
028 import org.relaxng.datatype.DatatypeStreamingValidator;
029 import org.whattf.datatype.DateOrTimeContent;
030 import org.whattf.datatype.Ratio;
031 import org.xml.sax.Attributes;
032 import org.xml.sax.SAXException;
033
034 /**
035 * Checks the <code>textContent</code> of elements whose
036 * <code>textContent</code> need special non-schema treatment. To smooth code
037 * reuse between a conformance checker and editors that only allow RELAX NG plus
038 * custom datatypes, this class uses objects that implement
039 * <code>DatatypeStreamingValidator</code>.
040 *
041 * <p>
042 * Examples of elements handled by this class are <code>time</code>,
043 * <code>meter</code> and <code>progress</code>.
044 *
045 * @version $Id: TextContentChecker.java 169 2007-05-25 07:21:55Z hsivonen $
046 * @author hsivonen
047 */
048 public final class TextContentChecker extends Checker {
049
050 /**
051 * The stack of <code>DatatypeStreamingValidator</code>s corresponding to
052 * open elements. Stack entry is <code>null</code> if the corresponding
053 * element does not need <code>textContent</code> checking. Grows from the
054 * tail.
055 */
056 private final LinkedList<DatatypeStreamingValidator> stack = new LinkedList<DatatypeStreamingValidator>();
057
058 /**
059 * Constructor.
060 */
061 public TextContentChecker() {
062 super();
063 }
064
065 /**
066 * Returns a <code>DatatypeStreamingValidator</code> for the element if it
067 * needs <code>textContent</code> checking or <code>null</code> if it does
068 * not.
069 *
070 * @param uri the namespace URI of the element
071 * @param localName the local name of the element
072 * @param atts the attributes
073 * @return a <code>DatatypeStreamingValidator</code> or <code>null</code> if
074 * checks not necessary
075 */
076 private DatatypeStreamingValidator streamingValidatorFor(String uri,
077 String localName, Attributes atts) {
078 if ("http://www.w3.org/1999/xhtml".equals(uri)) {
079 if ("meter".equals(localName) || "progress".equals(localName)) {
080 if (atts.getIndex("", "value") < 0) {
081 return Ratio.THE_INSTANCE.createStreamingValidator(null);
082 }
083 } else if ("time".equals(localName)) {
084 if (atts.getIndex("", "datetime") < 0) {
085 return DateOrTimeContent.THE_INSTANCE.createStreamingValidator(null);
086 }
087 }
088 }
089 return null;
090 }
091
092 /**
093 * @see org.whattf.checker.Checker#characters(char[], int, int)
094 */
095 public void characters(char[] ch, int start, int length)
096 throws SAXException {
097 for (DatatypeStreamingValidator dsv : stack) {
098 if (dsv != null) {
099 dsv.addCharacters(ch, start, length);
100 }
101 }
102 }
103
104 /**
105 * @see org.whattf.checker.Checker#endElement(java.lang.String,
106 * java.lang.String, java.lang.String)
107 */
108 public void endElement(String uri, String localName, String qName)
109 throws SAXException {
110 DatatypeStreamingValidator dsv = stack.removeLast();
111 if (dsv != null) {
112 try {
113 dsv.checkValid();
114 } catch (DatatypeException e) {
115 String msg = e.getMessage();
116 if (msg == null) {
117 err("The text content of element \u201C" + localName
118 + "\u201D from namespace \u201C" + uri
119 + "\u201D was not in the required format.");
120 } else {
121 err("The text content of element \u201C" + localName
122 + "\u201D from namespace \u201C" + uri
123 + "\u201D was not in the required format: " + msg);
124 }
125 }
126 }
127 }
128
129 /**
130 * @see org.whattf.checker.Checker#startElement(java.lang.String,
131 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
132 */
133 public void startElement(String uri, String localName, String qName,
134 Attributes atts) throws SAXException {
135 stack.addLast(streamingValidatorFor(uri, localName, atts));
136 }
137
138 /**
139 * @see org.whattf.checker.Checker#reset()
140 */
141 public void reset() {
142 stack.clear();
143 }
144
145 }