Clover coverage report - dom4j - 1.6.1
Coverage timestamp: ma mei 16 2005 14:23:01 GMT+01:00
file stats: LOC: 679   Methods: 24
NCLOC: 270   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
STAXEventReader.java 32,7% 47,6% 41,7% 43,1%
coverage coverage
 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.io;
 9   
 10    import java.io.InputStream;
 11    import java.io.Reader;
 12    import java.util.Iterator;
 13   
 14    import javax.xml.namespace.QName;
 15    import javax.xml.stream.XMLEventReader;
 16    import javax.xml.stream.XMLInputFactory;
 17    import javax.xml.stream.XMLStreamConstants;
 18    import javax.xml.stream.XMLStreamException;
 19    import javax.xml.stream.events.Attribute;
 20    import javax.xml.stream.events.Characters;
 21    import javax.xml.stream.events.Comment;
 22    import javax.xml.stream.events.EndElement;
 23    import javax.xml.stream.events.EntityReference;
 24    import javax.xml.stream.events.Namespace;
 25    import javax.xml.stream.events.ProcessingInstruction;
 26    import javax.xml.stream.events.StartDocument;
 27    import javax.xml.stream.events.StartElement;
 28    import javax.xml.stream.events.XMLEvent;
 29   
 30    import org.dom4j.CharacterData;
 31    import org.dom4j.Document;
 32    import org.dom4j.DocumentFactory;
 33    import org.dom4j.Element;
 34    import org.dom4j.Entity;
 35    import org.dom4j.Node;
 36   
 37    /**
 38    * Reads a DOM4J {@link Document}, as well as other {@link Node}s, from a StAX
 39    * {@link XMLEventReader}.
 40    *
 41    * @author Christian Niles
 42    */
 43    public class STAXEventReader {
 44    /** Reference to the DocumentFactory used to build DOM4J nodes. */
 45    private DocumentFactory factory;
 46   
 47    /** A StAX input factory, used to construct streams from IO streams. */
 48    private XMLInputFactory inputFactory = XMLInputFactory.newInstance();
 49   
 50    /**
 51    * Constructs a default <code>STAXEventReader</code> instance with a
 52    * default {@link DocumentFactory}.
 53    */
 54  1 public STAXEventReader() {
 55  1 this.factory = DocumentFactory.getInstance();
 56    }
 57   
 58    /**
 59    * Constructs a <code>STAXEventReader</code> instance that uses the
 60    * specified {@link DocumentFactory}to construct DOM4J {@link Node}s.
 61    *
 62    * @param factory
 63    * The DocumentFactory to use when constructing DOM4J nodes, or
 64    * <code>null</code> if a default should be used.
 65    */
 66  0 public STAXEventReader(DocumentFactory factory) {
 67  0 if (factory != null) {
 68  0 this.factory = factory;
 69    } else {
 70  0 this.factory = DocumentFactory.getInstance();
 71    }
 72    }
 73   
 74    /**
 75    * Sets the DocumentFactory to be used when constructing DOM4J nodes.
 76    *
 77    * @param documentFactory
 78    * The DocumentFactory to use when constructing DOM4J nodes, or
 79    * <code>null</code> if a default should be used.
 80    */
 81  0 public void setDocumentFactory(DocumentFactory documentFactory) {
 82  0 if (documentFactory != null) {
 83  0 this.factory = documentFactory;
 84    } else {
 85  0 this.factory = DocumentFactory.getInstance();
 86    }
 87    }
 88   
 89    /**
 90    * Constructs a StAX event stream from the provided I/O stream and reads a
 91    * DOM4J document from it.
 92    *
 93    * @param is
 94    * The I/O stream from which the Document will be read.
 95    *
 96    * @return The Document that was read from the stream.
 97    *
 98    * @throws XMLStreamException
 99    * If an error occurs reading content from the stream.
 100    */
 101  0 public Document readDocument(InputStream is) throws XMLStreamException {
 102  0 return readDocument(is, null);
 103    }
 104   
 105    /**
 106    * Constructs a StAX event stream from the provided I/O character stream and
 107    * reads a DOM4J document from it.
 108    *
 109    * @param reader
 110    * The character stream from which the Document will be read.
 111    *
 112    * @return The Document that was read from the stream.
 113    *
 114    * @throws XMLStreamException
 115    * If an error occurs reading content from the stream.
 116    */
 117  1 public Document readDocument(Reader reader) throws XMLStreamException {
 118  1 return readDocument(reader, null);
 119    }
 120   
 121    /**
 122    * Constructs a StAX event stream from the provided I/O stream and reads a
 123    * DOM4J document from it.
 124    *
 125    * @param is
 126    * The I/O stream from which the Document will be read.
 127    * @param systemId
 128    * A system id used to resolve entities.
 129    *
 130    * @return The Document that was read from the stream.
 131    *
 132    * @throws XMLStreamException
 133    * If an error occurs reading content from the stream.
 134    */
 135  0 public Document readDocument(InputStream is, String systemId)
 136    throws XMLStreamException {
 137  0 XMLEventReader eventReader = inputFactory.createXMLEventReader(
 138    systemId, is);
 139   
 140  0 try {
 141  0 return readDocument(eventReader);
 142    } finally {
 143  0 eventReader.close();
 144    }
 145    }
 146   
 147    /**
 148    * Constructs a StAX event stream from the provided I/O character stream and
 149    * reads a DOM4J document from it.
 150    *
 151    * @param reader
 152    * The character stream from which the Document will be read.
 153    * @param systemId
 154    * A system id used to resolve entities.
 155    *
 156    * @return The Document that was read from the stream.
 157    *
 158    * @throws XMLStreamException
 159    * If an error occurs reading content from the stream.
 160    */
 161  1 public Document readDocument(Reader reader, String systemId)
 162    throws XMLStreamException {
 163  1 XMLEventReader eventReader = inputFactory.createXMLEventReader(
 164    systemId, reader);
 165   
 166  1 try {
 167  1 return readDocument(eventReader);
 168    } finally {
 169  1 eventReader.close();
 170    }
 171    }
 172   
 173    /**
 174    * Reads a {@link Node}from the event stream. If the next event is a
 175    * {@link StartElement}, all events until the closing {@link EndElement}
 176    * will be read, and the resulting nodes will be added to the returned
 177    * {@link Element}.
 178    *
 179    * <p>
 180    * <strong>Pre-Conditions </strong>: The stream must be positioned before an
 181    * event other than an <code>EndElement</code>,<code>EndDocument</code>,
 182    * or any DTD-related events, which are not currently supported.
 183    * </p>
 184    *
 185    * @param reader
 186    * The reader from which events will be read.
 187    *
 188    * @return A DOM4J {@link Node}constructed from the read events.
 189    *
 190    * @throws XMLStreamException
 191    * If an error occurs reading from the stream, or the stream was
 192    * positioned before an unsupported event.
 193    */
 194  24 public Node readNode(XMLEventReader reader) throws XMLStreamException {
 195  24 XMLEvent event = reader.peek();
 196   
 197  24 if (event.isStartElement()) {
 198  8 return readElement(reader);
 199  16 } else if (event.isCharacters()) {
 200  16 return readCharacters(reader);
 201  0 } else if (event.isStartDocument()) {
 202  0 return readDocument(reader);
 203  0 } else if (event.isProcessingInstruction()) {
 204  0 return readProcessingInstruction(reader);
 205  0 } else if (event.isEntityReference()) {
 206  0 return readEntityReference(reader);
 207  0 } else if (event.isAttribute()) {
 208  0 return readAttribute(reader);
 209  0 } else if (event.isNamespace()) {
 210  0 return readNamespace(reader);
 211    } else {
 212  0 throw new XMLStreamException("Unsupported event: " + event);
 213    }
 214    }
 215   
 216    /**
 217    * Reads a DOM4J {@link Document}from the provided stream. The stream
 218    * should be positioned at the start of a document, or before a {@link
 219    * StartElement} event.
 220    *
 221    * @param reader
 222    * The event stream from which to read the {@link Document}.
 223    *
 224    * @return The {@link Document}that was read from the stream.
 225    *
 226    * @throws XMLStreamException
 227    * If an error occurs reading events from the stream.
 228    */
 229  1 public Document readDocument(XMLEventReader reader)
 230    throws XMLStreamException {
 231  1 Document doc = null;
 232   
 233  1 while (reader.hasNext()) {
 234  5 XMLEvent nextEvent = reader.peek();
 235  5 int type = nextEvent.getEventType();
 236   
 237  5 switch (type) {
 238  1 case XMLStreamConstants.START_DOCUMENT:
 239   
 240  1 StartDocument event = (StartDocument) reader.nextEvent();
 241   
 242  1 if (doc == null) {
 243    // create document
 244  1 if (event.encodingSet()) {
 245  1 String encodingScheme = event
 246    .getCharacterEncodingScheme();
 247  1 doc = factory.createDocument(encodingScheme);
 248    } else {
 249  0 doc = factory.createDocument();
 250    }
 251    } else {
 252    // duplicate or misplaced xml declaration
 253  0 String msg = "Unexpected StartDocument event";
 254  0 throw new XMLStreamException(msg, event.getLocation());
 255    }
 256   
 257  1 break;
 258   
 259  1 case XMLStreamConstants.END_DOCUMENT:
 260  0 case XMLStreamConstants.SPACE:
 261  2 case XMLStreamConstants.CHARACTERS:
 262   
 263    // skip end document and space outside the root element
 264  3 reader.nextEvent();
 265   
 266  3 break;
 267   
 268  1 default:
 269   
 270  1 if (doc == null) {
 271    // create document
 272  0 doc = factory.createDocument();
 273    }
 274   
 275  1 Node n = readNode(reader);
 276  1 doc.add(n);
 277    }
 278    }
 279   
 280  1 return doc;
 281    }
 282   
 283    /**
 284    * Reads a DOM4J Element from the provided event stream. The stream must be
 285    * positioned before an {@link StartElement}event. In addition to the
 286    * initial start event, all events up to and including the closing {@link
 287    * EndElement} will be read, and included with the returned element.
 288    *
 289    * @param eventReader
 290    * The event stream from which to read the Element.
 291    *
 292    * @return The Element that was read from the stream.
 293    *
 294    * @throws XMLStreamException
 295    * If an error occured reading events from the stream, or the
 296    * stream was not positioned before a {@linkStartElement}event.
 297    */
 298  8 public Element readElement(XMLEventReader eventReader)
 299    throws XMLStreamException {
 300  8 XMLEvent event = eventReader.peek();
 301   
 302  8 if (event.isStartElement()) {
 303    // advance the reader and get the StartElement event
 304  8 StartElement startTag = eventReader.nextEvent().asStartElement();
 305  8 Element elem = createElement(startTag);
 306   
 307    // read element content
 308  8 while (true) {
 309  31 if (!eventReader.hasNext()) {
 310  0 String msg = "Unexpected end of stream while reading"
 311    + " element content";
 312  0 throw new XMLStreamException(msg);
 313    }
 314   
 315  31 XMLEvent nextEvent = eventReader.peek();
 316   
 317  31 if (nextEvent.isEndElement()) {
 318  8 EndElement endElem = eventReader.nextEvent().asEndElement();
 319   
 320  8 if (!endElem.getName().equals(startTag.getName())) {
 321  0 throw new XMLStreamException("Expected "
 322    + startTag.getName() + " end-tag, but found"
 323    + endElem.getName());
 324    }
 325   
 326  8 break;
 327    }
 328   
 329  23 Node child = readNode(eventReader);
 330  23 elem.add(child);
 331    }
 332   
 333  8 return elem;
 334    } else {
 335  0 throw new XMLStreamException("Expected Element event, found: "
 336    + event);
 337    }
 338    }
 339   
 340    /**
 341    * Constructs a DOM4J Attribute from the provided event stream. The stream
 342    * must be positioned before an {@link Attribute}event.
 343    *
 344    * @param reader
 345    * The event stream from which to read the Attribute.
 346    *
 347    * @return The Attribute that was read from the stream.
 348    *
 349    * @throws XMLStreamException
 350    * If an error occured reading events from the stream, or the
 351    * stream was not positioned before an {@linkAttribute}event.
 352    */
 353  0 public org.dom4j.Attribute readAttribute(XMLEventReader reader)
 354    throws XMLStreamException {
 355  0 XMLEvent event = reader.peek();
 356   
 357  0 if (event.isAttribute()) {
 358  0 Attribute attr = (Attribute) reader.nextEvent();
 359   
 360  0 return createAttribute(null, attr);
 361    } else {
 362  0 throw new XMLStreamException("Expected Attribute event, found: "
 363    + event);
 364    }
 365    }
 366   
 367    /**
 368    * Constructs a DOM4J Namespace from the provided event stream. The stream
 369    * must be positioned before a {@link Namespace}event.
 370    *
 371    * @param reader
 372    * The event stream from which to read the Namespace.
 373    *
 374    * @return The Namespace that was read from the stream.
 375    *
 376    * @throws XMLStreamException
 377    * If an error occured reading events from the stream, or the
 378    * stream was not positioned before a {@linkNamespace}event.
 379    */
 380  0 public org.dom4j.Namespace readNamespace(XMLEventReader reader)
 381    throws XMLStreamException {
 382  0 XMLEvent event = reader.peek();
 383   
 384  0 if (event.isNamespace()) {
 385  0 Namespace ns = (Namespace) reader.nextEvent();
 386   
 387  0 return createNamespace(ns);
 388    } else {
 389  0 throw new XMLStreamException("Expected Namespace event, found: "
 390    + event);
 391    }
 392    }
 393   
 394    /**
 395    * Constructs a DOM4J Text or CDATA section from the provided event stream.
 396    * The stream must be positioned before a {@link Characters}event.
 397    *
 398    * @param reader
 399    * The event stream from which to read the Text or CDATA.
 400    *
 401    * @return The Text or CDATA that was read from the stream.
 402    *
 403    * @throws XMLStreamException
 404    * If an error occured reading events from the stream, or the
 405    * stream was not positioned before a {@linkCharacters}event.
 406    */
 407  16 public CharacterData readCharacters(XMLEventReader reader)
 408    throws XMLStreamException {
 409  16 XMLEvent event = reader.peek();
 410   
 411  16 if (event.isCharacters()) {
 412  16 Characters characters = reader.nextEvent().asCharacters();
 413   
 414  16 return createCharacterData(characters);
 415    } else {
 416  0 throw new XMLStreamException("Expected Characters event, found: "
 417    + event);
 418    }
 419    }
 420   
 421    /**
 422    * Constructs a DOM4J Comment from the provided event stream. The stream
 423    * must be positioned before a {@link Comment}event.
 424    *
 425    * @param reader
 426    * The event stream from which to read the Comment.
 427    *
 428    * @return The Comment that was read from the stream.
 429    *
 430    * @throws XMLStreamException
 431    * If an error occured reading events from the stream, or the
 432    * stream was not positioned before a {@linkComment}event.
 433    */
 434  0 public org.dom4j.Comment readComment(XMLEventReader reader)
 435    throws XMLStreamException {
 436  0 XMLEvent event = reader.peek();
 437   
 438  0 if (event instanceof Comment) {
 439  0 return createComment((Comment) reader.nextEvent());
 440    } else {
 441  0 throw new XMLStreamException("Expected Comment event, found: "
 442    + event);
 443    }
 444    }
 445   
 446    /**
 447    * Constructs a DOM4J Entity from the provided event stream. The stream must
 448    * be positioned before an {@link EntityReference}event.
 449    *
 450    * @param reader
 451    * The event stream from which to read the {@link
 452    * EntityReference}.
 453    *
 454    * @return The {@link org.dom4j.Entity}that was read from the stream.
 455    *
 456    * @throws XMLStreamException
 457    * If an error occured reading events from the stream, or the
 458    * stream was not positioned before an {@linkEntityReference}
 459    * event.
 460    */
 461  0 public Entity readEntityReference(XMLEventReader reader)
 462    throws XMLStreamException {
 463  0 XMLEvent event = reader.peek();
 464   
 465  0 if (event.isEntityReference()) {
 466  0 EntityReference entityRef = (EntityReference) reader.nextEvent();
 467   
 468  0 return createEntity(entityRef);
 469    } else {
 470  0 throw new XMLStreamException("Expected EntityRef event, found: "
 471    + event);
 472    }
 473    }
 474   
 475    /**
 476    * Constructs a DOM4J ProcessingInstruction from the provided event stream.
 477    * The stream must be positioned before a {@link ProcessingInstruction}
 478    * event.
 479    *
 480    * @param reader
 481    * The event stream from which to read the ProcessingInstruction.
 482    *
 483    * @return The ProcessingInstruction that was read from the stream.
 484    *
 485    * @throws XMLStreamException
 486    * If an error occured reading events from the stream, or the
 487    * stream was not positioned before a {@link
 488    * ProcessingInstruction} event.
 489    */
 490  0 public org.dom4j.ProcessingInstruction readProcessingInstruction(
 491    XMLEventReader reader) throws XMLStreamException {
 492  0 XMLEvent event = reader.peek();
 493   
 494  0 if (event.isProcessingInstruction()) {
 495  0 ProcessingInstruction pi = (ProcessingInstruction) reader
 496    .nextEvent();
 497   
 498  0 return createProcessingInstruction(pi);
 499    } else {
 500  0 throw new XMLStreamException("Expected PI event, found: " + event);
 501    }
 502    }
 503   
 504    /**
 505    * Constructs a new DOM4J Element from the provided StartElement event. All
 506    * attributes and namespaces will be added to the returned element.
 507    *
 508    * @param startEvent
 509    * The StartElement event from which to construct the new DOM4J
 510    * Element.
 511    *
 512    * @return The Element constructed from the provided StartElement event.
 513    */
 514  8 public Element createElement(StartElement startEvent) {
 515  8 QName qname = startEvent.getName();
 516  8 org.dom4j.QName elemName = createQName(qname);
 517   
 518  8 Element elem = factory.createElement(elemName);
 519   
 520    // create attributes
 521  8 for (Iterator i = startEvent.getAttributes(); i.hasNext();) {
 522  0 Attribute attr = (Attribute) i.next();
 523  0 elem.addAttribute(createQName(attr.getName()), attr.getValue());
 524    }
 525   
 526    // create namespaces
 527  8 for (Iterator i = startEvent.getNamespaces(); i.hasNext();) {
 528  0 Namespace ns = (Namespace) i.next();
 529  0 elem.addNamespace(ns.getPrefix(), ns.getNamespaceURI());
 530    }
 531   
 532  8 return elem;
 533    }
 534   
 535    /**
 536    * Constructs a new DOM4J Attribute from the provided StAX Attribute event.
 537    *
 538    * @param elem
 539    * DOCUMENT ME!
 540    * @param attr
 541    * The Attribute event from which to construct the new DOM4J
 542    * Attribute.
 543    *
 544    * @return The Attribute constructed from the provided Attribute event.
 545    */
 546  0 public org.dom4j.Attribute createAttribute(Element elem, Attribute attr) {
 547  0 return factory.createAttribute(elem, createQName(attr.getName()), attr
 548    .getValue());
 549    }
 550   
 551    /**
 552    * Constructs a new DOM4J Namespace from the provided StAX Namespace event.
 553    *
 554    * @param ns
 555    * The Namespace event from which to construct the new DOM4J
 556    * Namespace.
 557    *
 558    * @return The Namespace constructed from the provided Namespace event.
 559    */
 560  0 public org.dom4j.Namespace createNamespace(Namespace ns) {
 561  0 return factory.createNamespace(ns.getPrefix(), ns.getNamespaceURI());
 562    }
 563   
 564    /**
 565    * Constructs a new DOM4J Text or CDATA object from the provided Characters
 566    * event.
 567    *
 568    * @param characters
 569    * The Characters event from which to construct the new DOM4J
 570    * Text or CDATA object.
 571    *
 572    * @return The Text or CDATA object constructed from the provided Characters
 573    * event.
 574    */
 575  16 public CharacterData createCharacterData(Characters characters) {
 576  16 String data = characters.getData();
 577   
 578  16 if (characters.isCData()) {
 579  0 return factory.createCDATA(data);
 580    } else {
 581  16 return factory.createText(data);
 582    }
 583    }
 584   
 585    /**
 586    * Constructs a new DOM4J Comment from the provided StAX Comment event.
 587    *
 588    * @param comment
 589    * The Comment event from which to construct the new DOM4J
 590    * Comment.
 591    *
 592    * @return The Comment constructed from the provided Comment event.
 593    */
 594  0 public org.dom4j.Comment createComment(Comment comment) {
 595  0 return factory.createComment(comment.getText());
 596    }
 597   
 598    /**
 599    * Constructs a new DOM4J Entity from the provided StAX EntityReference
 600    * event.
 601    *
 602    * @param entityRef
 603    * The EntityReference event from which to construct the new
 604    * DOM4J Entity.
 605    *
 606    * @return The Entity constructed from the provided EntityReference event.
 607    */
 608  0 public org.dom4j.Entity createEntity(EntityReference entityRef) {
 609  0 return factory.createEntity(entityRef.getName(), entityRef
 610    .getDeclaration().getReplacementText());
 611    }
 612   
 613    /**
 614    * Constructs a new DOM4J ProcessingInstruction from the provided StAX
 615    * ProcessingInstruction event.
 616    *
 617    * @param pi
 618    * The ProcessingInstruction event from which to construct the
 619    * new DOM4J ProcessingInstruction.
 620    *
 621    * @return The ProcessingInstruction constructed from the provided
 622    * ProcessingInstruction event.
 623    */
 624  0 public org.dom4j.ProcessingInstruction createProcessingInstruction(
 625    ProcessingInstruction pi) {
 626  0 return factory
 627    .createProcessingInstruction(pi.getTarget(), pi.getData());
 628    }
 629   
 630    /**
 631    * Constructs a new DOM4J QName from the provided JAXP QName.
 632    *
 633    * @param qname
 634    * The JAXP QName from which to create a DOM4J QName.
 635    *
 636    * @return The newly constructed DOM4J QName.
 637    */
 638  8 public org.dom4j.QName createQName(QName qname) {
 639  8 return factory.createQName(qname.getLocalPart(), qname.getPrefix(),
 640    qname.getNamespaceURI());
 641    }
 642    }
 643   
 644    /*
 645    * Redistribution and use of this software and associated documentation
 646    * ("Software"), with or without modification, are permitted provided that the
 647    * following conditions are met:
 648    *
 649    * 1. Redistributions of source code must retain copyright statements and
 650    * notices. Redistributions must also contain a copy of this document.
 651    *
 652    * 2. Redistributions in binary form must reproduce the above copyright notice,
 653    * this list of conditions and the following disclaimer in the documentation
 654    * and/or other materials provided with the distribution.
 655    *
 656    * 3. The name "DOM4J" must not be used to endorse or promote products derived
 657    * from this Software without prior written permission of MetaStuff, Ltd. For
 658    * written permission, please contact dom4j-info@metastuff.com.
 659    *
 660    * 4. Products derived from this Software may not be called "DOM4J" nor may
 661    * "DOM4J" appear in their names without prior written permission of MetaStuff,
 662    * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
 663    *
 664    * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
 665    *
 666    * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
 667    * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 668    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 669    * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
 670    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 671    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 672    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 673    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 674    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 675    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 676    * POSSIBILITY OF SUCH DAMAGE.
 677    *
 678    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
 679    */