001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.XPathContext;
004: import net.sf.saxon.trans.DynamicError;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.type.*;
007: import net.sf.saxon.functions.EscapeURI;
008:
009: import java.net.MalformedURLException;
010: import java.net.URI;
011: import java.net.URISyntaxException;
012: import java.net.URL;
013:
014: /**
015: * An XPath value of type xs:anyURI.
016: *
017: * <p>This is implemented as a subtype of StringValue even though xs:anyURI is not a subtype of
018: * xs:string in the XPath type hierarchy. This enables type promotion from URI to String to happen
019: * automatically in most cases where it is appropriate.</p>
020: *
021: * <p>This implementation of xs:anyURI allows any string to be contained in the value space. It is possible
022: * to validate that the string is a "valid URI" in the sense of XML Schema Part 2 (which refers to the XLink
023: * specification and to RFC 2396); however, this validation is optional, and is not carried out by default.
024: * In particular, there is no constraint that namespace URIs, collation URIs, and the like should be valid
025: * URIs. However, casting from strings to xs:anyURI does invoke validation.</p>
026: */
027:
028: public final class AnyURIValue extends StringValue {
029:
030: public static final AnyURIValue EMPTY_URI = new AnyURIValue("");
031:
032: /**
033: * Constructor
034: * @param value the String value. Null is taken as equivalent to "".
035: */
036:
037: public AnyURIValue(CharSequence value) {
038: this .value = (value == null ? "" : trimWhitespace(value)
039: .toString());
040: }
041:
042: /**
043: * Check whether a string consititutes a valid URI
044: */
045:
046: public static boolean isValidURI(CharSequence value) {
047:
048: String sv = value.toString().trim();
049:
050: // Allow zero-length strings (RFC2396 is ambivalent on this point)
051: if (sv.length() == 0) {
052: return true;
053: }
054:
055: // Allow a string if the java.net.URI class accepts it
056: try {
057: new URI(sv);
058: return true;
059: } catch (URISyntaxException e) {
060: // keep trying
061: }
062:
063: // Allow a string if it can be escaped into a form that java.net.URI accepts
064: sv = EscapeURI.escape(sv, false).toString();
065: try {
066: new URI(sv);
067: return true;
068: } catch (URISyntaxException e) {
069: return false;
070: }
071: }
072:
073: /**
074: * Convert to target data type
075: * @param requiredType integer code representing the item type required
076: * @param context
077: * @return the result of the conversion, or an ErrorValue
078: */
079:
080: public AtomicValue convertPrimitive(BuiltInAtomicType requiredType,
081: boolean validate, XPathContext context) {
082: int req = requiredType.getPrimitiveType();
083: switch (req) {
084: case Type.ANY_ATOMIC:
085: case Type.ITEM:
086: case Type.ANY_URI:
087: return this ;
088: case Type.UNTYPED_ATOMIC:
089: return new UntypedAtomicValue(value);
090: case Type.STRING:
091: return new StringValue(value);
092: case Type.NORMALIZED_STRING:
093: case Type.TOKEN:
094: case Type.LANGUAGE:
095: case Type.NAME:
096: case Type.NCNAME:
097: case Type.ID:
098: case Type.IDREF:
099: case Type.ENTITY:
100: case Type.NMTOKEN:
101: return RestrictedStringValue.makeRestrictedString(value,
102: req, (validate ? context.getConfiguration()
103: .getNameChecker() : null));
104:
105: default:
106: ValidationException err = new ValidationException(
107: "Cannot convert anyURI to "
108: + requiredType.getDisplayName());
109: err.setErrorCode("XPTY0004");
110: return new ValidationErrorValue(err);
111: }
112: }
113:
114: /**
115: * Return the type of the expression
116: * @return Type.ANY_URI_TYPE (always)
117: * @param th
118: */
119:
120: public ItemType getItemType(TypeHierarchy th) {
121: return Type.ANY_URI_TYPE;
122: }
123:
124: /**
125: * Determine if two AnyURIValues are equal, according to XML Schema rules. (This method
126: * is not used for XPath comparisons, which are always under the control of a collation.)
127: * @throws ClassCastException if the values are not comparable
128: */
129:
130: public boolean equals(Object other) {
131: // Force a ClassCastException if the other value isn't an anyURI or derived from anyURI
132: AnyURIValue otherVal = (AnyURIValue) ((AtomicValue) other)
133: .getPrimitiveValue();
134: // cannot use equals() directly on two unlike CharSequences
135: return getStringValue().equals(otherVal.getStringValue());
136: }
137:
138: /**
139: * Get the effective boolean value of the value
140: *
141: * @param context the evaluation context (not used in this implementation)
142: * @return true, unless the value is boolean false, numeric zero, or
143: * zero-length string
144: */
145: public boolean effectiveBooleanValue(XPathContext context)
146: throws XPathException {
147: DynamicError err = new DynamicError(
148: "Effective boolean value is not defined for a value of type xs:anyURI");
149: err.setIsTypeError(true);
150: err.setErrorCode("FORG0006");
151: err.setXPathContext(context);
152: throw err;
153: }
154:
155: /**
156: * Convert to Java object (for passing to external functions)
157: * @param target the Java class to which conversion is required
158: * @return the result of the conversion
159: * @throws XPathException if conversion to this target type is not possible
160: */
161:
162: public Object convertToJava(Class target, XPathContext context)
163: throws XPathException {
164: if (target == Object.class) {
165: return value;
166: } else if (target.isAssignableFrom(StringValue.class)) {
167: return this ;
168: } else if (target == URI.class) {
169: try {
170: return new URI(value.toString());
171: } catch (URISyntaxException err) {
172: throw new DynamicError("The anyURI value '" + value
173: + "' is not an acceptable Java URI");
174: }
175: } else if (target == URL.class) {
176: try {
177: return new URL(value.toString());
178: } catch (MalformedURLException err) {
179: throw new DynamicError("The anyURI value '" + value
180: + "' is not an acceptable Java URL");
181: }
182: } else if (target == String.class) {
183: return value;
184: } else if (target == CharSequence.class) {
185: return value;
186: } else {
187: Object o = super .convertToJava(target, context);
188: if (o == null) {
189: throw new DynamicError("Conversion of anyURI to "
190: + target.getName() + " is not supported");
191: }
192: return o;
193: }
194: }
195:
196: }
197:
198: //
199: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
200: // you may not use this file except in compliance with the License. You may obtain a copy of the
201: // License at http://www.mozilla.org/MPL/
202: //
203: // Software distributed under the License is distributed on an "AS IS" basis,
204: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
205: // See the License for the specific language governing rights and limitations under the License.
206: //
207: // The Original Code is: all this file.
208: //
209: // The Initial Developer of the Original Code is Michael H. Kay
210: //
211: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
212: //
213: // Contributor(s): none.
214: //
|