Source code: com/hp/hpl/jena/datatypes/xsd/XSDDatatype.java
1 /******************************************************************
2 * File: XSDDatatype.java
3 * Created by: Dave Reynolds
4 * Created on: 09-Dec-02
5 *
6 * (c) Copyright 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
7 * [See end of file]
8 * $Id: XSDDatatype.java,v 1.9 2005/02/21 12:02:15 andy_seaborne Exp $
9 *****************************************************************/
10
11 package com.hp.hpl.jena.datatypes.xsd;
12
13 import java.math.BigDecimal;
14 import java.math.BigInteger;
15 import java.io.Reader;
16 import java.util.*;
17
18 import com.hp.hpl.jena.datatypes.*;
19 import com.hp.hpl.jena.datatypes.xsd.impl.*;
20 import com.hp.hpl.jena.graph.impl.LiteralLabel;
21
22 import org.apache.xerces.impl.dv.util.Base64;
23 import org.apache.xerces.impl.dv.util.HexBin;
24 import org.apache.xerces.impl.dv.xs.DecimalDV;
25 import org.apache.xerces.impl.dv.xs.XSSimpleTypeDecl;
26 import org.apache.xerces.impl.dv.*;
27 import org.apache.xerces.impl.validation.ValidationState;
28 import org.apache.xerces.util.SymbolHash;
29
30 import org.apache.xerces.parsers.XMLGrammarPreparser;
31 import org.apache.xerces.xni.grammars.XMLGrammarDescription;
32 import org.apache.xerces.xni.parser.XMLInputSource;
33 import org.apache.xerces.xs.XSConstants;
34 import org.apache.xerces.xs.XSTypeDefinition;
35 import org.apache.xerces.xs.XSNamedMap;
36 import org.apache.xerces.xni.grammars.XSGrammar;
37
38 /**
39 * Representation of an XSD datatype based on the Xerces-2
40 * XSD implementation.
41 *
42 * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
43 * @version $Revision: 1.9 $ on $Date: 2005/02/21 12:02:15 $
44 */
45 public class XSDDatatype extends BaseDatatype {
46
47 //=======================================================================
48 // Global statics - define single instance for each import XSD type
49
50 /** The xsd namespace */
51 public static final String XSD = "http://www.w3.org/2001/XMLSchema";
52
53 /** Datatype representing xsd:float */
54 public static final XSDDatatype XSDfloat = new XSDFloat("float", Float.class);
55
56 /** Datatype representing xsd:double */
57 public static final XSDDatatype XSDdouble = new XSDDouble("double", Double.class);
58
59 /** Datatype representing xsd:int */
60 public static final XSDDatatype XSDint = new XSDBaseNumericType("int", Integer.class);
61
62 /** Datatype representing xsd:long */
63 public static final XSDDatatype XSDlong = new XSDBaseNumericType("long", Long.class);
64
65 /** Datatype representing xsd:short */
66 public static final XSDDatatype XSDshort = new XSDBaseNumericType("short", Short.class);
67
68 /** Datatype representing xsd:byte */
69 public static final XSDDatatype XSDbyte = new XSDByteType("byte", Byte.class);
70
71 /** Datatype representing xsd:unsignedByte */
72 public static final XSDDatatype XSDunsignedByte = new XSDBaseNumericType("unsignedByte");
73
74 /** Datatype representing xsd:unsignedShort */
75 public static final XSDDatatype XSDunsignedShort = new XSDBaseNumericType("unsignedShort");
76
77 /** Datatype representing xsd:unsignedInt */
78 public static final XSDDatatype XSDunsignedInt = new XSDBaseNumericType("unsignedInt");
79
80 /** Datatype representing xsd:unsignedLong */
81 public static final XSDDatatype XSDunsignedLong = new XSDBaseNumericType("unsignedLong");
82
83 /** Datatype representing xsd:decimal */
84 public static final XSDDatatype XSDdecimal = new XSDBaseNumericType("decimal", BigDecimal.class);
85
86 /** Datatype representing xsd:integer */
87 public static final XSDDatatype XSDinteger = new XSDBaseNumericType("integer", BigInteger.class);
88
89 /** Datatype representing xsd:nonPositiveInteger */
90 public static final XSDDatatype XSDnonPositiveInteger = new XSDBaseNumericType("nonPositiveInteger");
91
92 /** Datatype representing xsd:nonNegativeInteger */
93 public static final XSDDatatype XSDnonNegativeInteger = new XSDBaseNumericType("nonNegativeInteger");
94
95 /** Datatype representing xsd:positiveInteger */
96 public static final XSDDatatype XSDpositiveInteger = new XSDBaseNumericType("positiveInteger");
97
98 /** Datatype representing xsd:negativeInteger */
99 public static final XSDDatatype XSDnegativeInteger = new XSDBaseNumericType("negativeInteger");
100
101 /** Datatype representing xsd:boolean */
102 public static final XSDDatatype XSDboolean = new XSDDatatype("boolean", Boolean.class);
103
104 /** Datatype representing xsd:string */
105 public static final XSDDatatype XSDstring = new XSDBaseStringType("string", String.class);
106
107 /** Datatype representing xsd:normalizedString */
108 public static final XSDDatatype XSDnormalizedString = new XSDBaseStringType("normalizedString", String.class);
109
110 /** Datatype representing xsd:anyURI */
111 public static final XSDDatatype XSDanyURI = new XSDDatatype("anyURI");
112
113 /** Datatype representing xsd:token */
114 public static final XSDDatatype XSDtoken = new XSDBaseStringType("token");
115
116 /** Datatype representing xsd:Name */
117 public static final XSDDatatype XSDName = new XSDBaseStringType("Name");
118
119 /** Datatype representing xsd:QName */
120 public static final XSDDatatype XSDQName = new XSDDatatype("QName");
121
122 /** Datatype representing xsd:language */
123 public static final XSDDatatype XSDlanguage = new XSDBaseStringType("language");
124
125 /** Datatype representing xsd:NMTOKEN */
126 public static final XSDDatatype XSDNMTOKEN = new XSDBaseStringType("NMTOKEN");
127
128 /** Datatype representing xsd:ENTITY */
129 public static final XSDDatatype XSDENTITY = new XSDBaseStringType("ENTITY");
130
131 /** Datatype representing xsd:ID */
132 public static final XSDDatatype XSDID = new XSDBaseStringType("ID");
133
134 /** Datatype representing xsd:NCName */
135 public static final XSDDatatype XSDNCName = new XSDBaseStringType("NCName");
136
137 /** Datatype representing xsd:IDREF */
138 public static final XSDDatatype XSDIDREF = new XSDDatatype("IDREF");
139
140 /** Datatype representing xsd:NOTATION */
141 public static final XSDDatatype XSDNOTATION = new XSDDatatype("NOTATION");
142
143 /** Datatype representing xsd:hexBinary */
144 public static final XSDDatatype XSDhexBinary = new XSDhexBinary("hexBinary");
145
146 /** Datatype representing xsd:base64Binary */
147 public static final XSDDatatype XSDbase64Binary = new XSDbase64Binary("base64Binary");
148
149 /** Datatype representing xsd:date */
150 public static final XSDDatatype XSDdate = new XSDDateType("date");
151
152 /** Datatype representing xsd:time */
153 public static final XSDDatatype XSDtime = new XSDTimeType("time");
154
155 /** Datatype representing xsd:dateTime */
156 public static final XSDDatatype XSDdateTime = new XSDDateTimeType("dateTime");
157
158 /** Datatype representing xsd:duration */
159 public static final XSDDatatype XSDduration = new XSDDurationType();
160
161 /** Datatype representing xsd:gDay */
162 public static final XSDDatatype XSDgDay = new XSDDayType("gDay");
163
164 /** Datatype representing xsd:gMonth */
165 public static final XSDDatatype XSDgMonth = new XSDMonthType("gMonth");
166
167 /** Datatype representing xsd:gYear */
168 public static final XSDDatatype XSDgYear = new XSDYearType("gYear");
169
170 /** Datatype representing xsd:gYearMonth */
171 public static final XSDDatatype XSDgYearMonth = new XSDYearMonthType("gYearMonth");
172
173 /** Datatype representing xsd:gMonthDay */
174 public static final XSDDatatype XSDgMonthDay = new XSDMonthDayType("gMonthDay");
175
176 // The following are list rather than simple types and are omitted for now
177
178 // /** Datatype representing xsd:ENTITIES */
179 // public static final XSDDatatype XSDENTITIES = new XSDBaseStringType("ENTITIES");
180 //
181 // /** Datatype representing xsd:NMTOKENS */
182 // public static final XSDDatatype XSDNMTOKENS = new XSDBaseStringType("NMTOKENS");
183 //
184 // /** Datatype representing xsd:IDREFS */
185 // public static final XSDDatatype XSDIDREFS = new XSDBaseStringType("IDREFS");
186
187 //=======================================================================
188 // local variables
189
190 /** the Xerces internal type declaration */
191 protected XSSimpleType typeDeclaration;
192
193 /** the corresponding java primitive class, if any */
194 protected Class javaClass = null;
195
196 /** Used to access the values and facets of any of the decimal numeric types */
197 static final DecimalDV decimalDV = new DecimalDV();
198
199 //=======================================================================
200 // Methods
201
202 /**
203 * Constructor.
204 * @param typeName the name of the XSD type to be instantiated, this is
205 * used to lookup a type definition from the Xerces schema factory.
206 */
207 public XSDDatatype(String typeName) {
208 super("");
209 typeDeclaration = SchemaDVFactory.getInstance().getBuiltInType(typeName);
210 uri = typeDeclaration.getNamespace() + "#" + typeDeclaration.getName();
211 }
212
213 /**
214 * Constructor.
215 * @param typeName the name of the XSD type to be instantiated, this is
216 * used to lookup a type definition from the Xerces schema factory.
217 * @param javaClass the java class for which this xsd type is to be
218 * treated as the cannonical representation
219 */
220 public XSDDatatype(String typeName, Class javaClass) {
221 this(typeName);
222 this.javaClass = javaClass;
223 }
224
225 /**
226 * Constructor used when loading in external user defined XSD types -
227 * should only be used by the internals but public scope because
228 * the internals spread across multiple packages.
229 *
230 * @param xstype the XSSimpleType definition to be wrapped
231 * @param namespace the namespace for the type (used because the grammar loading doesn't seem to keep that)
232 */
233 public XSDDatatype(XSSimpleType xstype, String namespace) {
234 super("");
235 typeDeclaration = xstype;
236 this.uri = namespace + "#" + typeDeclaration.getName();
237 }
238
239 /**
240 * Parse a lexical form of this datatype to a value
241 * @throws DatatypeFormatException if the lexical form is not legal
242 */
243 public Object parse(String lexicalForm) throws DatatypeFormatException {
244 try {
245 ValidationContext context = new ValidationState();
246 ValidatedInfo resultInfo = new ValidatedInfo();
247 Object result = typeDeclaration.validate(lexicalForm, context, resultInfo);
248 return convertValidatedDataValue(resultInfo);
249 } catch (InvalidDatatypeValueException e) {
250 throw new DatatypeFormatException(lexicalForm, this, "during parse -" + e);
251 }
252 }
253
254 /**
255 * Convert a value of this datatype out
256 * to lexical form.
257 */
258 public String unparse(Object value) {
259 return value.toString();
260 }
261
262 /**
263 * Compares two instances of values of the given datatype.
264 * This ignores lang tags and defers to the equality function
265 * defined by the Xerces package - to be checked.
266 */
267 public boolean isEqual(LiteralLabel value1, LiteralLabel value2) {
268 return typeDeclaration.isEqual(value1.getValue(), value2.getValue());
269 }
270
271 /**
272 * If this datatype is used as the cannonical representation
273 * for a particular java datatype then return that java type,
274 * otherwise returns null.
275 */
276 public Class getJavaClass() {
277 return javaClass;
278 }
279
280 /**
281 * Returns the Xerces datatype representation for this type, this
282 * is an XSSimpleType, in fact an XSSimpleTypeDecl.
283 */
284 public Object extendedTypeDefinition() {
285 return typeDeclaration;
286 }
287
288 /**
289 * Create and register a set of types specified in a user schema file.
290 * We use the (illegal) DAML+OIL approach that the uriref of the type
291 * is the url of the schema file with fragment ID corresponding the
292 * the name of the type.
293 *
294 * @param uri the absolute uri of the schema file to be loaded
295 * @param reader the Reader stream onto the file (useful if you wish to load a cached copy of the schema file)
296 * @param encoding the encoding of the source file (can be null)
297 * @param tm the type mapper into which to load the definitions
298 * @return a List of strings giving the uri's of the newly defined datatypes
299 * @throws DatatypeFormatException if there is a problem during load (not that we use Xerces
300 * in default mode for load which may provide diagnostic output direct to stderr)
301 */
302 public static List loadUserDefined(String uri, Reader reader, String encoding, TypeMapper tm) throws DatatypeFormatException {
303 return loadUserDefined(new XMLInputSource(null, uri, uri, reader, encoding), tm);
304 }
305
306 /**
307 * Create and register a set of types specified in a user schema file.
308 * We use the (illegal) DAML+OIL approach that the uriref of the type
309 * is the url of the schema file with fragment ID corresponding the
310 * the name of the type.
311 *
312 * @param uri the absolute uri of the schema file to be loaded, this should be a resolvable URL
313 * @param encoding the encoding of the source file (can be null)
314 * @param tm the type mapper into which to load the definitions
315 * @return a List of strings giving the uri's of the newly defined datatypes
316 * @throws DatatypeFormatException if there is a problem during load (not that we use Xerces
317 * in default mode for load which may provide diagnostic output direct to stderr)
318 */
319 public static List loadUserDefined(String uri, String encoding, TypeMapper tm) throws DatatypeFormatException {
320 return loadUserDefined(new XMLInputSource(null, uri, uri), tm);
321 }
322
323 /**
324 * Internal implementation of loadUserDefined
325 *
326 * @param uri the absolute uri of the schema file to be loaded
327 * @param reader the Reader stream onto the file (useful if you wish to load a cached copy of the schema file)
328 * @param encoding the encoding of the source file (can be null)
329 * @param tm the type mapper into which to load the definitions
330 * @return a List of strings giving the uri's of the newly defined datatypes
331 * @throws DatatypeFormatException if there is a problem during load (not that we use Xerces
332 * in default mode for load which may provide diagnostic output direct to stderr)
333 */
334 private static List loadUserDefined(XMLInputSource source, TypeMapper tm) throws DatatypeFormatException {
335 XMLGrammarPreparser parser = new XMLGrammarPreparser();
336 parser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null);
337 try {
338 XSGrammar xsg = (XSGrammar) parser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA, source);
339 org.apache.xerces.xs.XSModel xsm = xsg.toXSModel();
340 XSNamedMap map = xsm.getComponents(XSTypeDefinition.SIMPLE_TYPE);
341 int numDefs = map.getLength();
342 ArrayList names = new ArrayList(numDefs);
343 for (int i = 0; i < numDefs; i++) {
344 XSSimpleType xstype = (XSSimpleType) map.item(i);
345 // Filter built in types - only needed for 2.6.0
346 if ( ! XSD.equals(xstype.getNamespace()) ) {
347 //xstype.derivedFrom()
348 XSDDatatype definedType = new XSDGenericType(xstype, source.getSystemId());
349 tm.registerDatatype(definedType);
350 names.add(definedType.getURI());
351 }
352 }
353 return names;
354 } catch (Exception e) {
355 e.printStackTrace(); // Temp
356 throw new DatatypeFormatException(e.toString());
357 }
358 }
359
360 /**
361 * Convert a validated xerces data value into the corresponding java data value.
362 * This function is currently the most blatently xerces-version dependent part
363 * of this subsystem. In many cases it also involves reparsing data which has
364 * already been parsed as part of the validation.
365 *
366 * @param validatedInfo a fully populated Xerces data validation context
367 * @return the appropriate java wrapper type
368 */
369 public Object convertValidatedDataValue(ValidatedInfo validatedInfo) throws DatatypeFormatException {
370 switch (validatedInfo.actualValueType) {
371 case XSConstants.BASE64BINARY_DT:
372 byte[] decoded = Base64.decode(validatedInfo.normalizedValue);
373 return (Object)(decoded);
374
375 case XSConstants.BOOLEAN_DT:
376 return (Boolean)validatedInfo.actualValue;
377
378 case XSConstants.HEXBINARY_DT:
379 decoded = HexBin.decode(validatedInfo.normalizedValue);
380 return (Object)(decoded);
381
382 case XSConstants.UNSIGNEDSHORT_DT:
383 case XSConstants.INT_DT:
384 return Integer.valueOf(trimPlus(validatedInfo.normalizedValue));
385
386 case XSConstants.UNSIGNEDINT_DT:
387 case XSConstants.LONG_DT:
388 return Long.valueOf(trimPlus(validatedInfo.normalizedValue));
389
390 case XSConstants.UNSIGNEDBYTE_DT:
391 case XSConstants.SHORT_DT:
392 return Short.valueOf(trimPlus(validatedInfo.normalizedValue));
393
394 case XSConstants.BYTE_DT:
395 return Byte.valueOf(trimPlus(validatedInfo.normalizedValue));
396
397 case XSConstants.UNSIGNEDLONG_DT:
398 case XSConstants.INTEGER_DT:
399 case XSConstants.NONNEGATIVEINTEGER_DT:
400 case XSConstants.NONPOSITIVEINTEGER_DT:
401 case XSConstants.POSITIVEINTEGER_DT:
402 case XSConstants.NEGATIVEINTEGER_DT:
403 case XSConstants.DECIMAL_DT:
404 Object xsdValue = validatedInfo.actualValue;
405 if (decimalDV.getTotalDigits(xsdValue) == 0) {
406 return new Long(0);
407 }
408 if (decimalDV.getFractionDigits(xsdValue) >= 1) {
409 return new BigDecimal(trimPlus(validatedInfo.normalizedValue));
410 }
411 // Can have 0 fractionDigits but still have a trailing .000
412 String lexical = trimPlus(validatedInfo.normalizedValue);
413 int dotx = lexical.indexOf('.');
414 if (dotx != -1) {
415 lexical = lexical.substring(0, dotx);
416 }
417 if (decimalDV.getTotalDigits(xsdValue) > 18) {
418 return new BigInteger(lexical);
419 } else {
420 return new Long(lexical);
421 }
422
423 default:
424 return parseValidated(validatedInfo.normalizedValue);
425 }
426 }
427
428 /**
429 * Parse a validated lexical form. Subclasses which use the default
430 * parse implementation and are not convered by the explicit convertValidatedData
431 * cases should override this.
432 */
433 public Object parseValidated(String lexical) {
434 return lexical;
435 }
436
437 /**
438 * Test whether the given LiteralLabel is a valid instance
439 * of this datatype. This takes into accound typing information
440 * as well as lexical form - for example an xsd:string is
441 * never considered valid as an xsd:integer (even if it is
442 * lexically legal like "1").
443 */
444 public boolean isValidLiteral(LiteralLabel lit) {
445 return isBaseTypeCompatible(lit) && isValid(lit.getLexicalForm());
446 }
447
448 /**
449 * Test if the given typed value is in the right partition of the XSD type space.
450 * If this test passes then if the typed value has a legal lexical form for
451 * this type then it is a legal instance.
452 */
453 public boolean isBaseTypeCompatible(LiteralLabel lit) {
454 XSTypeDefinition base = getFoundingType();
455 RDFDatatype litDT = lit.getDatatype();
456 if (litDT instanceof XSDDatatype) {
457 XSTypeDefinition litBase = ((XSDDatatype)litDT).getFoundingType();
458 return base.equals(litBase);
459
460 } else if (litDT == null && lit.language().equals("")) {
461 // Special RDF case, a plain literal is type compatible with and xsd:string-based type
462 return base.equals(XSDstring.typeDeclaration);
463 } else {
464 return false;
465 }
466 }
467
468 /**
469 * Return the most specific type below xsd:anySimpleType that this type is derived from.
470 */
471 private XSTypeDefinition getFoundingType() {
472 XSTypeDefinition founding = typeDeclaration;
473 XSTypeDefinition parent = founding.getBaseType();
474 while (parent.getBaseType() != null) {
475 founding = parent;
476 parent = founding.getBaseType();
477 }
478 return founding;
479 }
480
481 /**
482 * Helper function to return the substring of a validated number string
483 * omitting any leading + sign.
484 */
485 public static String trimPlus(String str) {
486 int i = str.indexOf('+');
487 if (i == -1) {
488 return str;
489 } else {
490 return str.substring(i+1);
491 }
492 }
493
494 /**
495 * Add all of the XSD pre-defined simple types to the given
496 * type mapper registry.
497 */
498 public static void loadXSDSimpleTypes(TypeMapper tm) {
499 tm.registerDatatype(new XSDDatatype("anySimpleType"));
500
501 tm.registerDatatype(XSDdecimal);
502 tm.registerDatatype(XSDinteger);
503 tm.registerDatatype(XSDnonPositiveInteger);
504 tm.registerDatatype(XSDnonNegativeInteger);
505 tm.registerDatatype(XSDpositiveInteger);
506 tm.registerDatatype(XSDnegativeInteger);
507
508 tm.registerDatatype(XSDbyte);
509 tm.registerDatatype(XSDunsignedByte);
510 tm.registerDatatype(XSDdouble);
511 tm.registerDatatype(XSDfloat);
512 tm.registerDatatype(XSDlong);
513 tm.registerDatatype(XSDunsignedInt);
514 tm.registerDatatype(XSDunsignedShort);
515 tm.registerDatatype(XSDunsignedLong);
516 tm.registerDatatype(XSDint);
517 tm.registerDatatype(XSDshort);
518
519 tm.registerDatatype(XSDboolean);
520 tm.registerDatatype(XSDbase64Binary);
521 tm.registerDatatype(XSDhexBinary);
522
523 tm.registerDatatype(XSDdate);
524 tm.registerDatatype(XSDtime);
525 tm.registerDatatype(XSDdateTime);
526 tm.registerDatatype(XSDduration);
527 tm.registerDatatype(XSDgYearMonth);
528 tm.registerDatatype(XSDgMonthDay);
529 tm.registerDatatype(XSDgMonth);
530 tm.registerDatatype(XSDgDay);
531 tm.registerDatatype(XSDgYear);
532
533 tm.registerDatatype(XSDnormalizedString);
534 tm.registerDatatype(XSDstring);
535 tm.registerDatatype(XSDanyURI);
536
537 tm.registerDatatype(XSDtoken);
538 tm.registerDatatype(XSDName);
539 tm.registerDatatype(XSDlanguage);
540 tm.registerDatatype(XSDQName);
541 tm.registerDatatype(XSDNMTOKEN);
542 tm.registerDatatype(XSDID);
543 tm.registerDatatype(XSDENTITY);
544 tm.registerDatatype(XSDNCName);
545 tm.registerDatatype(XSDNOTATION);
546 tm.registerDatatype(XSDIDREF);
547
548 // tm.registerDatatype(XSDIDREFS);
549 // tm.registerDatatype(XSDENTITIES);
550 // tm.registerDatatype(XSDNMTOKENS);
551 }
552
553 // Temporary - used bootstrap the above initialization code
554 public static void main(String[] args) {
555 SymbolHash types = SchemaDVFactory.getInstance().getBuiltInTypes();
556 int len = types.getLength();
557 Object[] values = new Object[len];
558 types.getValues(values, 0);
559 for (int i = 0; i < values.length; i++) {
560 if (values[i] instanceof XSSimpleTypeDecl) {
561 XSSimpleTypeDecl decl = (XSSimpleTypeDecl)values[i];
562 System.out.println("tm.registerDatatype(new XSDDatatype(\""
563 + decl.getName()
564 + "\"));");
565 } else {
566 System.out.println(" - " + values[i]);
567 }
568 }
569 }
570
571 }
572
573 /*
574 (c) Copyright 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
575 All rights reserved.
576
577 Redistribution and use in source and binary forms, with or without
578 modification, are permitted provided that the following conditions
579 are met:
580
581 1. Redistributions of source code must retain the above copyright
582 notice, this list of conditions and the following disclaimer.
583
584 2. Redistributions in binary form must reproduce the above copyright
585 notice, this list of conditions and the following disclaimer in the
586 documentation and/or other materials provided with the distribution.
587
588 3. The name of the author may not be used to endorse or promote products
589 derived from this software without specific prior written permission.
590
591 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
592 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
593 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
594 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
595 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
596 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
597 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
598 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
599 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
600 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
601 */