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 fi.iki.hsivonen.xml.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,v 1.6 2006/12/01 12:34:31 hsivonen Exp $ 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 fi.iki.hsivonen.xml.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 fi.iki.hsivonen.xml.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 fi.iki.hsivonen.xml.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 fi.iki.hsivonen.xml.checker.Checker#reset() 140 */ 141 public void reset() { 142 stack.clear(); 143 } 144 145 }