/*
* Copyright (c) 2006 Henri Sivonen
* Copyright (c) 2007-2009 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package org.whattf.datatype;
import java.util.ArrayList;
import java.util.List;
import org.relaxng.datatype.Datatype;
import org.relaxng.datatype.DatatypeException;
import org.relaxng.datatype.DatatypeStreamingValidator;
import org.relaxng.datatype.ValidationContext;
/**
* Common superclass for HTML5 datatypes. Implements all methods of the
* <code>Datatype</code> interface and leaves a new <code>checkValid</code> for
* subclasses to implement.
*
* @version $Id$
* @author hsivonen
*/
public abstract class AbstractDatatype implements Datatype {
/**
* Constructor
*/
AbstractDatatype() {
super();
}
/**
* Calls <code>checkValid(CharSequence literal)</code>.
* @param literal the value
* @param context the validation context (ignored by subclasses)
* @return <code>true</code> if valid and <code>false</code> if not
* @see org.relaxng.datatype.Datatype#isValid(java.lang.String, org.relaxng.datatype.ValidationContext)
*/
public final boolean isValid(String literal, ValidationContext context) {
try {
checkValid(literal);
} catch (DatatypeException e) {
return false;
}
return true;
}
/**
* Delegates to <code>checkValid(CharSequence literal)</code>.
* @param literal the value
* @param context the validation context (ignored by subclasses)
* @throws DatatypeException if the literal does not conform to the datatype definition
* @see org.relaxng.datatype.Datatype#checkValid(java.lang.String, org.relaxng.datatype.ValidationContext)
*/
public void checkValid(String literal, ValidationContext context) throws DatatypeException {
checkValid(literal);
}
public abstract void checkValid(CharSequence literal) throws DatatypeException;
/**
* Merely returns a <code>DatatypeStreamingValidatorImpl</code>.
* @param context the validation context (ignored by subclasses)
* @return An unoptimized <code>DatatypeStreamingValidator</code>
* @see org.relaxng.datatype.Datatype#createStreamingValidator(org.relaxng.datatype.ValidationContext)
*/
public DatatypeStreamingValidator createStreamingValidator(
ValidationContext context) {
return new DatatypeStreamingValidatorImpl(this);
}
/**
* Implements strict string equality semantics by returning <code>literal</code>
* itself.
* @param literal the value (get returned)
* @param context ignored
* @return the <code>literal</code> that was passed in
* @see org.relaxng.datatype.Datatype#createValue(java.lang.String, org.relaxng.datatype.ValidationContext)
*/
public Object createValue(String literal, ValidationContext context) {
return literal;
}
/**
* Implements strict string equality semantics by performing a standard
* <code>equals</code> check on the arguments.
* @param value1 an object returned by <code>createValue</code>
* @param value2 another object returned by <code>createValue</code>
* @return <code>true</code> if the values are equal, <code>false</code> otherwise
* @see org.relaxng.datatype.Datatype#sameValue(java.lang.Object, java.lang.Object)
*/
public final boolean sameValue(Object value1, Object value2) {
if (value1 == null) {
return (value2 == null);
}
return value1.equals(value2);
}
/**
* Implements strict stirng equality semantics by returning the
* <code>java.lang.Object</code>-level <code>hashCode</code> of
* the object.
* @param value an object returned by <code>createValue</code>
* @return the hash code
* @see org.relaxng.datatype.Datatype#valueHashCode(java.lang.Object)
*/
public final int valueHashCode(Object value) {
return value.hashCode();
}
/**
* Always returns <code>Datatype.ID_TYPE_NULL</code>.
* @return <code>Datatype.ID_TYPE_NULL</code>
* @see org.relaxng.datatype.Datatype#getIdType()
*/
public final int getIdType() {
return Datatype.ID_TYPE_NULL;
}
/**
* Returns <code>false</code>
* @return <code>false</code>
* @see org.relaxng.datatype.Datatype#isContextDependent()
*/
public boolean isContextDependent() {
return false;
}
/**
* Checks if a UTF-16 code unit represents a whitespace character (U+0020,
* U+0009, U+000C, U+000D or U+000A).
* @param c the code unit
* @return <code>true</code> if whitespace, <code>false</code> otherwise
*/
protected final boolean isWhitespace(char c) {
return c == ' ' || c == '\t' || c == '\f' || c == '\n' || c == '\r';
}
protected final boolean isAsciiDigit(char c) {
return c >= '0' && c <= '9';
}
protected static final char toAsciiLowerCase(char c) {
if (c >= 'A' && c <= 'Z') {
c += 0x20;
}
return c;
}
protected static final String toAsciiLowerCase(CharSequence str) {
if (str == null) {
return null;
}
char[] buf = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 0x20;
}
buf[i] = c;
}
return new String(buf);
}
protected static final char toAsciiUpperCase(char c) {
if (c >= 'a' && c <= 'z') {
c -= 0x20;
}
return c;
}
protected static final String toAsciiUpperCase(CharSequence str) {
if (str == null) {
return null;
}
char[] buf = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c >= 'a' && c <= 'z') {
c -= 0x20;
}
buf[i] = c;
}
return new String(buf);
}
protected DatatypeException newDatatypeException(String message) {
return new Html5DatatypeException(this.getClass(), this.getName(), message);
}
protected DatatypeException newDatatypeException(String head, String literal, String tail) {
return new Html5DatatypeException(this.getClass(), this.getName(), head, literal, tail);
}
protected DatatypeException newDatatypeException(String head, char literal, String tail) {
return new Html5DatatypeException(this.getClass(), this.getName(), head, String.valueOf(literal), tail);
}
protected DatatypeException newDatatypeException(int position, String message) {
return new Html5DatatypeException(position, this.getClass(), this.getName(), message);
}
protected DatatypeException newDatatypeException(int position, String head, String literal, String tail) {
return new Html5DatatypeException(position, this.getClass(), this.getName(), head, literal, tail);
}
protected DatatypeException newDatatypeException(int position, String head, char literal, String tail) {
return new Html5DatatypeException(position, this.getClass(), this.getName(), head, String.valueOf(literal), tail);
}
/* for datatype exceptions that are handled as warnings, the following are
* alternative forms of all the above, with an additional "warning" parameter */
protected DatatypeException newDatatypeException(String message, boolean warning) {
return new Html5DatatypeException(this.getClass(), this.getName(), message, warning);
}
protected DatatypeException newDatatypeException(String head, String literal, String tail, boolean warning) {
return new Html5DatatypeException(this.getClass(), this.getName(), head, literal, tail, warning);
}
protected DatatypeException newDatatypeException(String head, char literal, String tail, boolean warning) {
return new Html5DatatypeException(this.getClass(), this.getName(), head, String.valueOf(literal), tail, warning);
}
protected DatatypeException newDatatypeException(int position, String message, boolean warning) {
return new Html5DatatypeException(position, this.getClass(), this.getName(), message, warning);
}
protected DatatypeException newDatatypeException(int position, String head, String literal, String tail, boolean warning) {
return new Html5DatatypeException(position, this.getClass(), this.getName(), head, literal, tail, warning);
}
protected DatatypeException newDatatypeException(int position, String head, char literal, String tail, boolean warning) {
return new Html5DatatypeException(position, this.getClass(), this.getName(), head, String.valueOf(literal), tail, warning);
}
public abstract String getName();
protected List<CharSequenceWithOffset> split(CharSequence sequence, char delimiter) {
List<CharSequenceWithOffset> rv = new ArrayList<CharSequenceWithOffset>();
int offset = 0;
for (int i = 0; i < sequence.length(); i++) {
char c = sequence.charAt(i);
if (c == delimiter) {
rv.add(new CharSequenceWithOffset(sequence.subSequence(offset, i), offset));
offset = i + 1;
}
}
rv.add(new CharSequenceWithOffset(sequence.subSequence(offset, sequence.length()), offset));
return rv;
}
protected class CharSequenceWithOffset {
private final CharSequence sequence;
private final int offset;
/**
* @param sequence
* @param offset
*/
public CharSequenceWithOffset(final CharSequence sequence, final int offset) {
this.sequence = sequence;
this.offset = offset;
}
/**
* Returns the offset.
*
* @return the offset
*/
public int getOffset() {
return offset;
}
/**
* Returns the sequence.
*
* @return the sequence
*/
public CharSequence getSequence() {
return sequence;
}
}
}