Clover coverage report - dom4j - 1.6.1
Coverage timestamp: ma mei 16 2005 14:23:01 GMT+01:00

file stats: | LOC: | 607 |  | Methods: | 18
| NCLOC: | 324 |  | Classes: | 1

Source file | Conditionals | Statements | Methods | TOTAL

SchemaParser.java

67,6%

71,6%

83,3%

71,3%

1 |  | /*
2 |  | * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3 |  | *
4 |  | * This software is open source.
5 |  | * See the bottom of this file for the licence.
6 |  | */
7 |  |
8 |  | package org.dom4j.datatype;
9 |  |
10 |  | import com.sun.msv.datatype.xsd.DatatypeFactory;
11 |  | import com.sun.msv.datatype.xsd.TypeIncubator;
12 |  | import com.sun.msv.datatype.xsd.XSDatatype;
13 |  |
14 |  | import java.util.HashMap;
15 |  | import java.util.Iterator;
16 |  | import java.util.Map;
17 |  |
18 |  | import org.dom4j.Attribute;
19 |  | import org.dom4j.Document;
20 |  | import org.dom4j.DocumentFactory;
21 |  | import org.dom4j.Element;
22 |  | import org.dom4j.Namespace;
23 |  | import org.dom4j.QName;
24 |  | import org.dom4j.io.SAXReader;
25 |  | import org.dom4j.util.AttributeHelper;
26 |  |
27 |  | import org.relaxng.datatype.DatatypeException;
28 |  | import org.relaxng.datatype.ValidationContext;
29 |  |
30 |  | import org.xml.sax.EntityResolver;
31 |  | import org.xml.sax.InputSource;
32 |  |
33 |  | /**
34 |  | * <p>
35 |  | * <code>SchemaParser</code> reads an XML Schema Document.
36 |  | * </p>
37 |  | *
38 |  | * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
39 |  | * @author Yuxin Ruan
40 |  | * @version $Revision: 1.19 $
41 |  | */
42 |  | public class SchemaParser {
43 |  | private static final Namespace XSD_NAMESPACE = Namespace.get("xsd",
44 |  | "http://www.w3.org/2001/XMLSchema");
45 |  |
46 |  | // Use QNames for the elements
47 |  | private static final QName XSD_ELEMENT = QName
48 |  | .get("element", XSD_NAMESPACE);
49 |  |
50 |  | private static final QName XSD_ATTRIBUTE = QName.get("attribute",
51 |  | XSD_NAMESPACE);
52 |  |
53 |  | private static final QName XSD_SIMPLETYPE = QName.get("simpleType",
54 |  | XSD_NAMESPACE);
55 |  |
56 |  | private static final QName XSD_COMPLEXTYPE = QName.get("complexType",
57 |  | XSD_NAMESPACE);
58 |  |
59 |  | private static final QName XSD_RESTRICTION = QName.get("restriction",
60 |  | XSD_NAMESPACE);
61 |  |
62 |  | private static final QName XSD_SEQUENCE = QName.get("sequence",
63 |  | XSD_NAMESPACE);
64 |  |
65 |  | private static final QName XSD_CHOICE = QName.get("choice", XSD_NAMESPACE);
66 |  |
67 |  | private static final QName XSD_ALL = QName.get("all", XSD_NAMESPACE);
68 |  |
69 |  | private static final QName XSD_INCLUDE = QName
70 |  | .get("include", XSD_NAMESPACE);
71 |  |
72 |  | /** Document factory used to register Element specific factories */
73 |  | private DatatypeDocumentFactory documentFactory;
74 |  |
75 |  | /**
76 |  | * Cache of <code>XSDatatype</code> instances loaded or created during
77 |  | * this build
78 |  | */
79 |  | private Map dataTypeCache = new HashMap();
80 |  |
81 |  | /** NamedTypeResolver */
82 |  | private NamedTypeResolver namedTypeResolver;
83 |  |
84 |  | /** target namespace */
85 |  | private Namespace targetNamespace;
86 |  |
87 | 0 | public SchemaParser() {
88 | 0 | this(DatatypeDocumentFactory.singleton);
89 |  | }
90 |  |
91 | 27 | public SchemaParser(DatatypeDocumentFactory documentFactory) {
92 | 27 | this.documentFactory = documentFactory;
93 | 27 | this.namedTypeResolver = new NamedTypeResolver(documentFactory);
94 |  | }
95 |  |
96 |  | /**
97 |  | * Parses the given schema document
98 |  | *
99 |  | * @param schemaDocument
100 |  | * is the document of the XML Schema
101 |  | */
102 | 22 | public void build(Document schemaDocument) {
103 | 22 | this.targetNamespace = null;
104 | 22 | internalBuild(schemaDocument);
105 |  | }
106 |  |
107 | 3 | public void build(Document schemaDocument, Namespace namespace) {
108 | 3 | this.targetNamespace = namespace;
109 | 3 | internalBuild(schemaDocument);
110 |  | }
111 |  |
112 | 25 | private synchronized void internalBuild(Document schemaDocument) {
113 | 25 | Element root = schemaDocument.getRootElement();
114 |  |
115 | 25 | if (root != null) {
116 |  | // handle schema includes
117 | 25 | Iterator includeIter = root.elementIterator(XSD_INCLUDE);
118 |  |
119 | 25 | while (includeIter.hasNext()) {
120 | 0 | Element includeElement = (Element) includeIter.next();
121 | 0 | String inclSchemaInstanceURI = includeElement
122 |  | .attributeValue("schemaLocation");
123 | 0 | EntityResolver resolver = schemaDocument.getEntityResolver();
124 |  |
125 | 0 | try {
126 | 0 | if (resolver == null) {
127 | 0 | String msg = "No EntityResolver available";
128 | 0 | throw new InvalidSchemaException(msg);
129 |  | }
130 |  |
131 | 0 | InputSource inputSource = resolver.resolveEntity(null,
132 |  | inclSchemaInstanceURI);
133 |  |
134 | 0 | if (inputSource == null) {
135 | 0 | String msg = "Could not resolve the schema URI: "
136 |  | + inclSchemaInstanceURI;
137 | 0 | throw new InvalidSchemaException(msg);
138 |  | }
139 |  |
140 | 0 | SAXReader reader = new SAXReader();
141 | 0 | Document inclSchemaDocument = reader.read(inputSource);
142 | 0 | build(inclSchemaDocument);
143 |  | } catch (Exception e) {
144 | 0 | System.out.println("Failed to load schema: "
145 |  | + inclSchemaInstanceURI);
146 | 0 | System.out.println("Caught: " + e);
147 | 0 | e.printStackTrace();
148 | 0 | throw new InvalidSchemaException("Failed to load schema: "
149 |  | + inclSchemaInstanceURI);
150 |  | }
151 |  | }
152 |  |
153 |  | // handle elements
154 | 25 | Iterator iter = root.elementIterator(XSD_ELEMENT);
155 |  |
156 | 25 | while (iter.hasNext()) {
157 | 180 | onDatatypeElement((Element) iter.next(), documentFactory);
158 |  | }
159 |  |
160 |  | // handle named simple types
161 | 25 | iter = root.elementIterator(XSD_SIMPLETYPE);
162 |  |
163 | 25 | while (iter.hasNext()) {
164 | 1 | onNamedSchemaSimpleType((Element) iter.next());
165 |  | }
166 |  |
167 |  | // hanlde named complex types
168 | 25 | iter = root.elementIterator(XSD_COMPLEXTYPE);
169 |  |
170 | 25 | while (iter.hasNext()) {
171 | 2 | onNamedSchemaComplexType((Element) iter.next());
172 |  | }
173 |  |
174 | 25 | namedTypeResolver.resolveNamedTypes();
175 |  | }
176 |  | }
177 |  |
178 |  | // Implementation methods
179 |  | // -------------------------------------------------------------------------
180 |  |
181 |  | /**
182 |  | * processes an XML Schema &lt;element&gt; tag
183 |  | *
184 |  | * @param xsdElement
185 |  | * DOCUMENT ME!
186 |  | * @param parentFactory
187 |  | * DOCUMENT ME!
188 |  | */
189 | 406 | private void onDatatypeElement(Element xsdElement,
190 |  | DocumentFactory parentFactory) {
191 | 406 | String name = xsdElement.attributeValue("name");
192 | 406 | String type = xsdElement.attributeValue("type");
193 | 406 | QName qname = getQName(name);
194 |  |
195 | 406 | DatatypeElementFactory factory = getDatatypeElementFactory(qname);
196 |  |
197 | 406 | if (type != null) {
198 |  | // register type with this element name
199 | 146 | XSDatatype dataType = getTypeByName(type);
200 |  |
201 | 146 | if (dataType != null) {
202 | 143 | factory.setChildElementXSDatatype(qname, dataType);
203 |  | } else {
204 | 3 | QName typeQName = getQName(type);
205 | 3 | namedTypeResolver.registerTypedElement(xsdElement, typeQName,
206 |  | parentFactory);
207 |  | }
208 |  |
209 | 146 | return;
210 |  | }
211 |  |
212 |  | // handle element types derrived from simpleTypes
213 | 260 | Element xsdSimpleType = xsdElement.element(XSD_SIMPLETYPE);
214 |  |
215 | 260 | if (xsdSimpleType != null) {
216 | 0 | XSDatatype dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
217 |  |
218 | 0 | if (dataType != null) {
219 | 0 | factory.setChildElementXSDatatype(qname, dataType);
220 |  | }
221 |  | }
222 |  |
223 | 260 | Element schemaComplexType = xsdElement.element(XSD_COMPLEXTYPE);
224 |  |
225 | 260 | if (schemaComplexType != null) {
226 | 107 | onSchemaComplexType(schemaComplexType, factory);
227 |  | }
228 |  |
229 | 260 | Iterator iter = xsdElement.elementIterator(XSD_ATTRIBUTE);
230 |  |
231 | 260 | if (iter.hasNext()) {
232 | 0 | do {
233 | 0 | onDatatypeAttribute(xsdElement, factory, (Element) iter
234 |  | .next());
235 | 0 | } while (iter.hasNext());
236 |  | }
237 |  | }
238 |  |
239 |  | /**
240 |  | * processes an named XML Schema &lt;complexTypegt; tag
241 |  | *
242 |  | * @param schemaComplexType
243 |  | * DOCUMENT ME!
244 |  | */
245 | 2 | private void onNamedSchemaComplexType(Element schemaComplexType) {
246 | 2 | Attribute nameAttr = schemaComplexType.attribute("name");
247 |  |
248 | 2 | if (nameAttr == null) {
249 | 0 | return;
250 |  | }
251 |  |
252 | 2 | String name = nameAttr.getText();
253 | 2 | QName qname = getQName(name);
254 |  |
255 | 2 | DatatypeElementFactory factory = getDatatypeElementFactory(qname);
256 |  |
257 | 2 | onSchemaComplexType(schemaComplexType, factory);
258 | 2 | namedTypeResolver.registerComplexType(qname, factory);
259 |  | }
260 |  |
261 |  | /**
262 |  | * processes an XML Schema &lt;complexTypegt; tag
263 |  | *
264 |  | * @param schemaComplexType
265 |  | * DOCUMENT ME!
266 |  | * @param elementFactory
267 |  | * DOCUMENT ME!
268 |  | */
269 | 109 | private void onSchemaComplexType(Element schemaComplexType,
270 |  | DatatypeElementFactory elementFactory) {
271 | 109 | Iterator iter = schemaComplexType.elementIterator(XSD_ATTRIBUTE);
272 |  |
273 | 109 | while (iter.hasNext()) {
274 | 265 | Element xsdAttribute = (Element) iter.next();
275 | 265 | String name = xsdAttribute.attributeValue("name");
276 | 265 | QName qname = getQName(name);
277 |  |
278 | 265 | XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute);
279 |  |
280 | 265 | if (dataType != null) {
281 |  | // register the XSDatatype for the given Attribute
282 |  | // #### should both these be done?
283 |  | // elementFactory.setChildElementXSDatatype( qname, dataType );
284 | 265 | elementFactory.setAttributeXSDatatype(qname, dataType);
285 |  | }
286 |  | }
287 |  |
288 |  | // handle sequence definition
289 | 109 | Element schemaSequence = schemaComplexType.element(XSD_SEQUENCE);
290 |  |
291 | 109 | if (schemaSequence != null) {
292 | 49 | onChildElements(schemaSequence, elementFactory);
293 |  | }
294 |  |
295 |  | // handle choice definition
296 | 109 | Element schemaChoice = schemaComplexType.element(XSD_CHOICE);
297 |  |
298 | 109 | if (schemaChoice != null) {
299 | 0 | onChildElements(schemaChoice, elementFactory);
300 |  | }
301 |  |
302 |  | // handle all definition
303 | 109 | Element schemaAll = schemaComplexType.element(XSD_ALL);
304 |  |
305 | 109 | if (schemaAll != null) {
306 | 19 | onChildElements(schemaAll, elementFactory);
307 |  | }
308 |  | }
309 |  |
310 | 68 | private void onChildElements(Element element, DatatypeElementFactory fact) {
311 | 68 | Iterator iter = element.elementIterator(XSD_ELEMENT);
312 |  |
313 | 68 | while (iter.hasNext()) {
314 | 226 | Element xsdElement = (Element) iter.next();
315 | 226 | onDatatypeElement(xsdElement, fact);
316 |  | }
317 |  | }
318 |  |
319 |  | /**
320 |  | * processes an XML Schema &lt;attribute&gt; tag
321 |  | *
322 |  | * @param xsdElement
323 |  | * DOCUMENT ME!
324 |  | * @param elementFactory
325 |  | * DOCUMENT ME!
326 |  | * @param xsdAttribute
327 |  | * DOCUMENT ME!
328 |  | */
329 | 0 | private void onDatatypeAttribute(Element xsdElement,
330 |  | DatatypeElementFactory elementFactory, Element xsdAttribute) {
331 | 0 | String name = xsdAttribute.attributeValue("name");
332 | 0 | QName qname = getQName(name);
333 | 0 | XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute);
334 |  |
335 | 0 | if (dataType != null) {
336 |  | // register the XSDatatype for the given Attribute
337 | 0 | elementFactory.setAttributeXSDatatype(qname, dataType);
338 |  | } else {
339 | 0 | String type = xsdAttribute.attributeValue("type");
340 | 0 | System.out.println("Warning: Couldn't find XSDatatype for type: "
341 |  | + type + " attribute: " + name);
342 |  | }
343 |  | }
344 |  |
345 |  | /**
346 |  | * processes an XML Schema &lt;attribute&gt; tag
347 |  | *
348 |  | * @param xsdAttribute
349 |  | * DOCUMENT ME!
350 |  | *
351 |  | * @return DOCUMENT ME!
352 |  | *
353 |  | * @throws InvalidSchemaException
354 |  | * DOCUMENT ME!
355 |  | */
356 | 265 | private XSDatatype dataTypeForXsdAttribute(Element xsdAttribute) {
357 | 265 | String type = xsdAttribute.attributeValue("type");
358 | 265 | XSDatatype dataType = null;
359 |  |
360 | 265 | if (type != null) {
361 | 209 | dataType = getTypeByName(type);
362 |  | } else {
363 |  | // must parse the <simpleType> element
364 | 56 | Element xsdSimpleType = xsdAttribute.element(XSD_SIMPLETYPE);
365 |  |
366 | 56 | if (xsdSimpleType == null) {
367 | 0 | String name = xsdAttribute.attributeValue("name");
368 | 0 | String msg = "The attribute: " + name
369 |  | + " has no type attribute and does not contain a "
370 |  | + "<simpleType/> element";
371 | 0 | throw new InvalidSchemaException(msg);
372 |  | }
373 |  |
374 | 56 | dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
375 |  | }
376 |  |
377 | 265 | return dataType;
378 |  | }
379 |  |
380 |  | /**
381 |  | * processes an named XML Schema &lt;simpleTypegt; tag
382 |  | *
383 |  | * @param schemaSimpleType
384 |  | * DOCUMENT ME!
385 |  | */
386 | 1 | private void onNamedSchemaSimpleType(Element schemaSimpleType) {
387 | 1 | Attribute nameAttr = schemaSimpleType.attribute("name");
388 |  |
389 | 1 | if (nameAttr == null) {
390 | 0 | return;
391 |  | }
392 |  |
393 | 1 | String name = nameAttr.getText();
394 | 1 | QName qname = getQName(name);
395 | 1 | XSDatatype datatype = loadXSDatatypeFromSimpleType(schemaSimpleType);
396 | 1 | namedTypeResolver.registerSimpleType(qname, datatype);
397 |  | }
398 |  |
399 |  | /**
400 |  | * Loads a XSDatatype object from a &lt;simpleType&gt; attribute schema
401 |  | * element
402 |  | *
403 |  | * @param xsdSimpleType
404 |  | * DOCUMENT ME!
405 |  | *
406 |  | * @return DOCUMENT ME!
407 |  | */
408 | 57 | private XSDatatype loadXSDatatypeFromSimpleType(Element xsdSimpleType) {
409 | 57 | Element xsdRestriction = xsdSimpleType.element(XSD_RESTRICTION);
410 |  |
411 | 57 | if (xsdRestriction != null) {
412 | 57 | String base = xsdRestriction.attributeValue("base");
413 |  |
414 | 57 | if (base != null) {
415 | 57 | XSDatatype baseType = getTypeByName(base);
416 |  |
417 | 57 | if (baseType == null) {
418 | 0 | onSchemaError("Invalid base type: " + base
419 |  | + " when trying to build restriction: "
420 |  | + xsdRestriction);
421 |  | } else {
422 | 57 | return deriveSimpleType(baseType, xsdRestriction);
423 |  | }
424 |  | } else {
425 |  | // simpleType and base are mutually exclusive and you
426 |  | // must have one within a <restriction> tag
427 | 0 | Element xsdSubType = xsdSimpleType.element(XSD_SIMPLETYPE);
428 |  |
429 | 0 | if (xsdSubType == null) {
430 | 0 | String msg = "The simpleType element: " + xsdSimpleType
431 |  | + " must contain a base attribute or simpleType"
432 |  | + " element";
433 | 0 | onSchemaError(msg);
434 |  | } else {
435 | 0 | return loadXSDatatypeFromSimpleType(xsdSubType);
436 |  | }
437 |  | }
438 |  | } else {
439 | 0 | onSchemaError("No <restriction>. Could not create XSDatatype for"
440 |  | + " simpleType: " + xsdSimpleType);
441 |  | }
442 |  |
443 | 0 | return null;
444 |  | }
445 |  |
446 |  | /**
447 |  | * Derives a new type from a base type and a set of restrictions
448 |  | *
449 |  | * @param baseType
450 |  | * DOCUMENT ME!
451 |  | * @param xsdRestriction
452 |  | * DOCUMENT ME!
453 |  | *
454 |  | * @return DOCUMENT ME!
455 |  | */
456 | 57 | private XSDatatype deriveSimpleType(XSDatatype baseType,
457 |  | Element xsdRestriction) {
458 | 57 | TypeIncubator incubator = new TypeIncubator(baseType);
459 | 57 | ValidationContext context = null;
460 |  |
461 | 57 | try {
462 | 57 | for (Iterator iter = xsdRestriction.elementIterator(); iter
463 |  | .hasNext();) {
464 | 103 | Element element = (Element) iter.next();
465 | 103 | String name = element.getName();
466 | 103 | String value = element.attributeValue("value");
467 | 103 | boolean fixed = AttributeHelper.booleanValue(element, "fixed");
468 |  |
469 |  | // add facet
470 | 103 | incubator.addFacet(name, value, fixed, context);
471 |  | }
472 |  |
473 |  | // derive a new type by those facets
474 | 57 | String newTypeName = null;
475 |  |
476 | 57 | return incubator.derive("", newTypeName);
477 |  | } catch (DatatypeException e) {
478 | 0 | onSchemaError("Invalid restriction: " + e.getMessage()
479 |  | + " when trying to build restriction: " + xsdRestriction);
480 |  |
481 | 0 | return null;
482 |  | }
483 |  | }
484 |  |
485 |  | /**
486 |  | * DOCUMENT ME!
487 |  | *
488 |  | * @param name
489 |  | * The name of the element
490 |  | *
491 |  | * @return the <code>DatatypeElementFactory</code> for the given element
492 |  | * QName, creating one if it does not already exist
493 |  | */
494 | 408 | private DatatypeElementFactory getDatatypeElementFactory(QName name) {
495 | 408 | DatatypeElementFactory factory = documentFactory
496 |  | .getElementFactory(name);
497 |  |
498 | 408 | if (factory == null) {
499 | 403 | factory = new DatatypeElementFactory(name);
500 | 403 | name.setDocumentFactory(factory);
501 |  | }
502 |  |
503 | 408 | return factory;
504 |  | }
505 |  |
506 | 412 | private XSDatatype getTypeByName(String type) {
507 | 412 | XSDatatype dataType = (XSDatatype) dataTypeCache.get(type);
508 |  |
509 | 412 | if (dataType == null) {
510 |  | // first check to see if it is a built-in type
511 |  | // maybe a prefix is being used
512 | 190 | int idx = type.indexOf(':');
513 |  |
514 | 190 | if (idx >= 0) {
515 | 50 | String localName = type.substring(idx + 1);
516 |  |
517 | 50 | try {
518 | 50 | dataType = DatatypeFactory.getTypeByName(localName);
519 |  | } catch (DatatypeException e) {
520 |  | }
521 |  | }
522 |  |
523 | 190 | if (dataType == null) {
524 | 140 | try {
525 | 140 | dataType = DatatypeFactory.getTypeByName(type);
526 |  | } catch (DatatypeException e) {
527 |  | }
528 |  | }
529 |  |
530 | 190 | if (dataType == null) {
531 |  | // it's no built-in type, maybe it's a type we defined ourself
532 | 3 | QName typeQName = getQName(type);
533 | 3 | dataType = (XSDatatype) namedTypeResolver.simpleTypeMap
534 |  | .get(typeQName);
535 |  | }
536 |  |
537 | 190 | if (dataType != null) {
538 |  | // store in cache for later
539 | 187 | dataTypeCache.put(type, dataType);
540 |  | }
541 |  | }
542 |  |
543 | 412 | return dataType;
544 |  | }
545 |  |
546 | 680 | private QName getQName(String name) {
547 | 680 | if (targetNamespace == null) {
548 | 596 | return documentFactory.createQName(name);
549 |  | } else {
550 | 84 | return documentFactory.createQName(name, targetNamespace);
551 |  | }
552 |  | }
553 |  |
554 |  | /**
555 |  | * Called when there is a problem with the schema and the builder cannot
556 |  | * handle the XML Schema Data Types correctly
557 |  | *
558 |  | * @param message
559 |  | * DOCUMENT ME!
560 |  | *
561 |  | * @throws InvalidSchemaException
562 |  | * DOCUMENT ME!
563 |  | */
564 | 0 | private void onSchemaError(String message) {
565 |  | // Some users may wish to disable exception throwing
566 |  | // and instead use some kind of listener for errors and continue
567 |  | // System.out.println( "WARNING: " + message );
568 | 0 | throw new InvalidSchemaException(message);
569 |  | }
570 |  | }
571 |  |
572 |  | /*
573 |  | * Redistribution and use of this software and associated documentation
574 |  | * ("Software"), with or without modification, are permitted provided that the
575 |  | * following conditions are met:
576 |  | *
577 |  | * 1. Redistributions of source code must retain copyright statements and
578 |  | * notices. Redistributions must also contain a copy of this document.
579 |  | *
580 |  | * 2. Redistributions in binary form must reproduce the above copyright notice,
581 |  | * this list of conditions and the following disclaimer in the documentation
582 |  | * and/or other materials provided with the distribution.
583 |  | *
584 |  | * 3. The name "DOM4J" must not be used to endorse or promote products derived
585 |  | * from this Software without prior written permission of MetaStuff, Ltd. For
586 |  | * written permission, please contact dom4j-info@metastuff.com.
587 |  | *
588 |  | * 4. Products derived from this Software may not be called "DOM4J" nor may
589 |  | * "DOM4J" appear in their names without prior written permission of MetaStuff,
590 |  | * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
591 |  | *
592 |  | * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
593 |  | *
594 |  | * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
595 |  | * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
596 |  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
597 |  | * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
598 |  | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
599 |  | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
600 |  | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
601 |  | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
602 |  | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
603 |  | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
604 |  | * POSSIBILITY OF SUCH DAMAGE.
605 |  | *
606 |  | * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
607 |  | */