001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.XPathContext;
004: import net.sf.saxon.om.FastStringBuffer;
005: import net.sf.saxon.om.NameChecker;
006: import net.sf.saxon.om.XMLChar;
007: import net.sf.saxon.type.*;
008:
009: /**
010: * A value conforming to one of the built-in subtypes of String, specifically
011: * normalizedString, token, language, Name, NCName, ID, IDREF, ENTITY, NMTOKEN.
012: * This class doesnt' handle the types derived by list: IDREFS, NMTOKENS, ENTITIES.
013: */
014:
015: public final class RestrictedStringValue extends StringValue {
016:
017: private int type;
018:
019: /**
020: * Factory method to create a restricted string value from a string
021: * @param value the String value. Null is taken as equivalent to "".
022: * @param checker a NameChecker if validation is required,
023: * null if the caller already knows that the value is valid
024: * @return either the required RestrictedStringValue if the value is valid, or an ErrorValue encapsulating
025: * the error message if not.
026: */
027:
028: public static AtomicValue makeRestrictedString(CharSequence value,
029: int type, NameChecker checker) {
030: RestrictedStringValue rsv = new RestrictedStringValue();
031: rsv.type = type;
032: if (value == null) {
033: rsv.value = "";
034: } else if (type == Type.NORMALIZED_STRING) {
035: rsv.value = normalizeWhitespace(value);
036: } else if (type == Type.TOKEN) {
037: rsv.value = collapseWhitespace(value);
038: } else {
039: rsv.value = trimWhitespace(value);
040: if (checker != null) {
041: ValidationException err = rsv.validate(checker);
042: if (err == null) {
043: return rsv;
044: } else {
045: return new ValidationErrorValue(err);
046: }
047: } else {
048: return rsv;
049: }
050: }
051: return rsv;
052: }
053:
054: /**
055: * Validate that the string conforms to the rules for its type
056: * @return null if the value is OK, otherwise a DynamicError containing details of the failure
057: */
058:
059: private ValidationException validate(NameChecker checker) {
060: switch (type) {
061:
062: case Type.TOKEN:
063: return null;
064: case Type.NORMALIZED_STRING:
065: return null;
066: case Type.LANGUAGE:
067: String regex = "(([a-z]|[A-Z])([a-z]|[A-Z])|" // ISO639Code
068: + "([iI]-([a-z]|[A-Z])+)|" // IanaCode
069: + "([xX]-([a-z]|[A-Z])+))" // UserCode
070: + "(-([a-z]|[A-Z])+)*"; // Subcode
071: if (!java.util.regex.Pattern.matches(regex, value
072: .toString())) {
073: ValidationException err = new ValidationException(
074: "The value '" + value
075: + "' is not a valid xs:language");
076: err.setErrorCode("FORG0001");
077: return err;
078: }
079: return null;
080: case Type.NAME:
081: // replace any colons by underscores and then test if it's a valid NCName
082: FastStringBuffer buff = new FastStringBuffer(value.length());
083: buff.append(value.toString());
084: for (int i = 0; i < buff.length(); i++) {
085: if (buff.charAt(i) == ':') {
086: buff.setCharAt(i, '_');
087: }
088: }
089: if (!checker.isValidNCName(buff.toString())) {
090: ValidationException err = new ValidationException(
091: "The value '" + value + "' is not a valid Name");
092: err.setErrorCode("FORG0001");
093: return err;
094: }
095: return null;
096: case Type.NCNAME:
097: case Type.ID:
098: case Type.IDREF:
099: case Type.ENTITY:
100: if (!checker.isValidNCName(value.toString())) {
101: ValidationException err = new ValidationException(
102: "The value '" + value
103: + "' is not a valid NCName");
104: err.setErrorCode("FORG0001");
105: return err;
106: }
107: return null;
108: case Type.NMTOKEN:
109: if (!XMLChar.isValidNmtoken(value.toString())) {
110: ValidationException err = new ValidationException(
111: "The value '" + value
112: + "' is not a valid NMTOKEN");
113: err.setErrorCode("FORG0001");
114: return err;
115: }
116: return null;
117: default:
118: throw new IllegalArgumentException(
119: "Unknown string value type " + type);
120: }
121: }
122:
123: /**
124: * Return the type of the expression
125: * @param th
126: */
127:
128: public ItemType getItemType(TypeHierarchy th) {
129: return (AtomicType) BuiltInSchemaFactory.getSchemaType(type);
130: }
131:
132: /**
133: * Convert to target data type
134: * @param requiredType an integer identifying the required atomic type
135: * @param context
136: * @return an AtomicValue, a value of the required type; or an ErrorValue
137: */
138:
139: public AtomicValue convertPrimitive(BuiltInAtomicType requiredType,
140: boolean validate, XPathContext context) {
141: int req = requiredType.getPrimitiveType();
142: if (req == Type.STRING) {
143: return StringValue.makeStringValue(value);
144: } else if (req == Type.UNTYPED_ATOMIC) {
145: return new UntypedAtomicValue(value);
146: } else {
147: return super .convertPrimitive(requiredType, validate,
148: context);
149: }
150: }
151:
152: public String toString() {
153: return getItemType(null).toString() + '(' + super .toString()
154: + ')';
155: }
156:
157: }
158:
159: //
160: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
161: // you may not use this file except in compliance with the License. You may obtain a copy of the
162: // License at http://www.mozilla.org/MPL/
163: //
164: // Software distributed under the License is distributed on an "AS IS" basis,
165: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
166: // See the License for the specific language governing rights and limitations under the License.
167: //
168: // The Original Code is: all this file.
169: //
170: // The Initial Developer of the Original Code is Michael H. Kay.
171: //
172: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
173: //
174: // Contributor(s): none.
175: //
|