Package org.exist.storage.dom

Source Code of org.exist.storage.dom.RawNodeIterator

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-2007 The eXist team
*  http://exist-db.org
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software Foundation
*  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*  $Id$
*/
package org.exist.storage.dom;

import org.apache.log4j.Logger;
import org.exist.dom.NodeHandle;
import org.exist.dom.NodeProxy;
import org.exist.storage.DBBroker;
import org.exist.storage.StorageAddress;
import org.exist.storage.btree.BTree;
import org.exist.storage.btree.BTreeException;
import org.exist.storage.btree.Paged;
import org.exist.storage.btree.Value;
import org.exist.storage.lock.Lock;
import org.exist.util.ByteConversion;
import org.exist.util.LockException;
import org.exist.util.sanity.SanityCheck;

import java.io.IOException;

/**
* An iterator that walks through the raw node data items in a document. The class
* keeps reading data items from the document's sequence of data pages until it encounters
* the end of the document. Each returned value contains the data of one node in the
* document.
*/
public class RawNodeIterator {

    private final static Logger LOG = Logger.getLogger(RawNodeIterator.class);

    private DOMFile db = null;
    private int offset;
    private short lastTupleID = ItemId.UNKNOWN_ID;
    private DOMFile.DOMPage page = null;
    private long pageNum;
    private DBBroker broker;

    /**
     * Construct the iterator. The iterator will be positioned before the specified
     * start node.
     *
     * @param broker the owner object used to acquire a lock on the underlying data file (usually a DBBroker)
     * @param db the underlying data file
     * @param node the start node where the iterator will be positioned.
     * @throws IOException
     */
    public RawNodeIterator(DBBroker broker, DOMFile db, NodeHandle node) throws IOException {
        this.db = db;
        this.broker = broker;
        seek(node);
    }

    /**
     * Reposition the iterator to the start of the specified node.
     *
     * @param node the start node where the iterator will be positioned.
     * @throws IOException
     */
    public void seek(NodeHandle node) throws IOException {
        final Lock lock = db.getLock();
        try {
            lock.acquire(Lock.READ_LOCK);
            RecordPos rec = null;
            if (StorageAddress.hasAddress(node.getInternalAddress()))
                {rec = db.findRecord(node.getInternalAddress());}
            if (rec == null) {
                try {
                    final long address = db.findValue(broker, new NodeProxy(node));
                    if (address == BTree.KEY_NOT_FOUND)
                        {throw new IOException("Node not found.");}
                    rec = db.findRecord(address);
                } catch (final BTreeException e) {
                    throw new IOException("Node not found: " + e.getMessage());
                }
            }
            pageNum = rec.getPage().getPageNum();
            //Position the stream at the very beginning of the record
            offset = rec.offset - DOMFile.LENGTH_TID;
            page = rec.getPage();
        } catch (final LockException e) {
            throw new IOException("Exception while scanning document: " + e.getMessage());
        } finally {
            lock.release(Lock.READ_LOCK);
        }
    }

    /**
     *  Returns the raw data of the next node in document order.
     * @return the raw data of the node
     */
    public Value next() {
        Value nextValue = null;
        final Lock lock = db.getLock();
        try {
            try {
                lock.acquire(Lock.READ_LOCK);
            } catch (final LockException e) {
                LOG.error("Failed to acquire read lock on " + db.getFile().getName());
                //TODO : throw exception here ? -pb
                return null;
            }
            db.setOwnerObject(broker);
            long backLink = 0;
            do {
                final DOMFile.DOMFilePageHeader pageHeader = page.getPageHeader();
                //Next value larger than length of the current page?
                if (offset >= pageHeader.getDataLength()) {
                    //Load next page in chain
                    long nextPage = pageHeader.getNextDataPage();
                    if (nextPage == Paged.Page.NO_PAGE) {
                        SanityCheck.TRACE("Bad link to next page " + page.page.getPageInfo() +
                            "; previous: " + pageHeader.getPreviousDataPage() +
                            "; offset = " + offset + "; lastTupleID = " + lastTupleID);
                        //TODO : throw exception here ? -pb
                        return null;
                    }
                    pageNum = nextPage;
                    page = db.getDOMPage(nextPage);
                    db.addToBuffer(page);
                    offset = 0;
                }
                //Extract the tuple id
                lastTupleID = ByteConversion.byteToShort(page.data, offset);
                offset += DOMFile.LENGTH_TID;
                //Check if this is just a link to a relocated node
                if(ItemId.isLink(lastTupleID)) {
                    //Skip this
                    offset += DOMFile.LENGTH_FORWARD_LOCATION;
                    continue;
                }
                //Read data length
                short valueLength = ByteConversion.byteToShort(page.data, offset);
                offset += DOMFile.LENGTH_DATA_LENGTH;
                if (valueLength < 0) {
                    LOG.error("Got negative length" + valueLength + " at offset " + offset + "!!!");
                    LOG.debug(db.debugPageContents(page));
                    //TODO : throw an exception right now ?
                }
                if (ItemId.isRelocated(lastTupleID)) {
                    // found a relocated node. Read the original address
                    backLink = ByteConversion.byteToLong(page.data, offset);
                    offset += DOMFile.LENGTH_ORIGINAL_LOCATION;
                }
                //Overflow page? load the overflow value
                if (valueLength == DOMFile.OVERFLOW) {
                    valueLength = DOMFile.LENGTH_OVERFLOW_LOCATION;
                    final long overflow = ByteConversion.byteToLong(page.data, offset);
                    offset += DOMFile.LENGTH_OVERFLOW_LOCATION;
                    try {
                        final byte[] odata = db.getOverflowValue(overflow);
                        nextValue = new Value(odata);
                    } catch(final Exception e) {
                        LOG.error("Exception while loading overflow value: " + e.getMessage() +
                            "; originating page: " + page.page.getPageInfo());
                    }
                    // normal node
                } else {
                    try {
                        nextValue = new Value(page.data, offset, valueLength);
                        offset += valueLength;
                    } catch(final Exception e) {
                        LOG.error("Error while deserializing node: " + e.getMessage(), e);
                        LOG.error("Reading from offset: " + offset + "; len = " + valueLength);
                        LOG.debug(db.debugPageContents(page));
                        throw new RuntimeException(e);
                    }
                }
                if (nextValue == null) {
                    LOG.error("illegal node on page " + page.getPageNum() +
                        "; tupleID = " + ItemId.getId(lastTupleID) +
                        "; next = " + page.getPageHeader().getNextDataPage() +
                        "; prev = " + page.getPageHeader().getPreviousDataPage() +
                        "; offset = " + (offset - valueLength) +
                        "; len = " + page.getPageHeader().getDataLength());
                    //TODO : throw exception here ? -pb
                    return null;
                }
                if (ItemId.isRelocated(lastTupleID)) {
                    nextValue.setAddress(backLink);
                } else {
                    nextValue.setAddress(StorageAddress.createPointer((int) pageNum,
                        ItemId.getId(lastTupleID))
                    );
                }
            } while (nextValue == null);
            return nextValue;
        } finally {
            lock.release(Lock.READ_LOCK);
        }
    }

    public void closeDocument() {
        db.closeDocument();
    }

    /**
     * Returns the internal virtual storage address of the node at the cursor's current
     * position.
     *
     * @return internal virtual storage address of the node
     */
    public long currentAddress() {
        return StorageAddress.createPointer((int) pageNum, ItemId.getId(lastTupleID));
    }
}
TOP

Related Classes of org.exist.storage.dom.RawNodeIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.