/* * eXist Open Source Native XML Database * * Copyright (C) 2000-04, Wolfgang Meier (wolfgang@exist-db.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ package org.exist.dom; import org.exist.collections.Collection; import org.exist.numbering.NodeId; import org.exist.security.Permission; import org.exist.storage.DBBroker; import org.exist.storage.lock.Lock; import org.exist.storage.lock.LockedDocumentMap; import org.exist.util.LockException; import org.exist.util.hashtable.Int2ObjectHashMap; import org.exist.xmldb.XmldbURI; import org.w3c.dom.Node; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.TreeSet; /** * Manages a set of documents. * * This class implements the NodeList interface for a collection of documents. * It also contains methods to retrieve the collections these documents * belong to. * * @author wolf */ public class DefaultDocumentSet extends Int2ObjectHashMap implements MutableDocumentSet { private ArrayList list = null; private TreeSet collections = new TreeSet(); public DefaultDocumentSet() { super(29, 1.75); } public DefaultDocumentSet(int initialSize) { super(initialSize, 1.75); } public void clear() { super.clear(); collections = new TreeSet(); list = null; } public void add(DocumentImpl doc) { add(doc, true); } public void add(DocumentImpl doc, boolean checkDuplicates) { final int docId = doc.getDocId(); if (checkDuplicates && containsKey(docId)) return; put(docId, doc); if (list != null) list.add(doc); if (doc.getCollection() != null && (!collections.contains(doc.getCollection()))) collections.add(doc.getCollection()); } public void add(Node node) { if (!(node instanceof DocumentImpl)) throw new RuntimeException("wrong implementation"); add((DocumentImpl) node); } public void addAll(DocumentSet other) { for (int i = 0; i < other.getDocumentCount(); i++) add(other.getDocumentAt(i)); } /** * Fast method to add a bunch of documents from a * Java collection. * * The method assumes that no duplicate entries are * in the input collection. */ public void addAll(DBBroker broker, Collection collection, String[] paths, boolean checkPermissions) { DocumentImpl doc; for (int i = 0; i < paths.length; i++) { doc = collection.getDocumentNoLock(paths[i]); if (doc == null) continue; if(broker == null || !checkPermissions || doc.getPermissions().validate(broker.getUser(), Permission.READ)) { // WM: we don't have a lock on the document, so we should not change its broker: // doc.setBroker(broker); put(doc.getDocId(), doc); } } } /** * Fast method to add a bunch of documents from a Java collection. * A lock will be acquired on each document. The locked document is added to the * specified LockedDocumentMap in order to keep track of the locks.. * * @param broker * @param collection * @param paths * @param lockMap * @param lockType * @throws LockException */ public void addAll(DBBroker broker, Collection collection, String[] paths, LockedDocumentMap lockMap, int lockType) throws LockException { DocumentImpl doc; Lock lock; for (int i = 0; i < paths.length; i++) { doc = collection.getDocumentNoLock(paths[i]); if (doc == null) continue; if (doc.getPermissions().validate(broker.getUser(), Permission.WRITE)) { lock = doc.getUpdateLock(); lock.acquire(Lock.WRITE_LOCK); put(doc.getDocId(), doc); lockMap.add(doc); } } } public void addCollection(Collection collection) { collections.add(collection); } public Iterator getDocumentIterator() { return valueIterator(); } public Iterator getCollectionIterator() { return collections.iterator(); } public int getDocumentCount() { return size(); } public int getCollectionCount() { return collections.size(); } public DocumentImpl getDocumentAt(int pos) { if (list == null) { list = new ArrayList(); for(Iterator i = valueIterator(); i.hasNext(); ) list.add(i.next()); } return (DocumentImpl) list.get(pos); } public DocumentImpl getDoc(int docId) { return (DocumentImpl) get(docId); } public XmldbURI[] getNames() { XmldbURI result[] = new XmldbURI[size()]; DocumentImpl d; int j = 0; for (Iterator i = getDocumentIterator(); i.hasNext(); j++) { d = (DocumentImpl) i.next(); result[j] = d.getFileURI(); } Arrays.sort(result); return result; } public DocumentSet intersection(DocumentSet other) { DefaultDocumentSet r = new DefaultDocumentSet(); DocumentImpl d; for (Iterator i = getDocumentIterator(); i.hasNext();) { d = (DocumentImpl) i.next(); if (other.contains(d.getDocId())) r.add(d); } for (Iterator i = other.getDocumentIterator(); i.hasNext();) { d = (DocumentImpl) i.next(); if (contains(d.getDocId()) && (!r.contains(d.getDocId()))) r.add(d); } return r; } public DocumentSet union(DocumentSet other) { DefaultDocumentSet result = new DefaultDocumentSet(); result.addAll(other); DocumentImpl d; for (Iterator i = getDocumentIterator(); i.hasNext();) { d = (DocumentImpl) i.next(); if (!result.contains(d.getDocId())) result.add(d); } return result; } public boolean contains(DocumentSet other) { if (other.getDocumentCount() > size()) return false; DocumentImpl d; for (Iterator i = other.getDocumentIterator(); i.hasNext();) { d = (DocumentImpl) i.next(); if (!contains(d.getDocId())) return false; } return true; } public boolean contains(int id) { return containsKey(id); } public NodeSet docsToNodeSet() { NodeSet result = new NewArrayNodeSet(getDocumentCount()); DocumentImpl doc; for (Iterator i = getDocumentIterator(); i.hasNext();) { doc = (DocumentImpl) i.next(); if(doc.getResourceType() == DocumentImpl.XML_FILE) { // skip binary resources result.add(new NodeProxy(doc, NodeId.DOCUMENT_NODE)); } } return result; } public int getMinDocId() { int min = DocumentImpl.UNKNOWN_DOCUMENT_ID; DocumentImpl d; for (Iterator i = getDocumentIterator(); i.hasNext();) { d = (DocumentImpl) i.next(); if (min == DocumentImpl.UNKNOWN_DOCUMENT_ID) min = d.getDocId(); else if (d.getDocId() < min) min = d.getDocId(); } return min; } public int getMaxDocId() { int max = DocumentImpl.UNKNOWN_DOCUMENT_ID; DocumentImpl d; for (Iterator i = getDocumentIterator(); i.hasNext();) { d = (DocumentImpl) i.next(); if (d.getDocId() > max) max = d.getDocId(); } return max; } public boolean equalDocs(DocumentSet other) { if (this == other) // we are comparing the same objects return true; if (size() != other.getDocumentCount()) return false; for(int idx = 0; idx < tabSize; idx++) { if (values[idx] == null || values[idx] == REMOVED) continue; if (!other.contains(keys[idx])) return false; } return true; } public void lock(DBBroker broker, boolean exclusive, boolean checkExisting) throws LockException { DocumentImpl d; Lock dlock; // final Thread thread = Thread.currentThread(); for(int idx = 0; idx < tabSize; idx++) { if(values[idx] == null || values[idx] == REMOVED) continue; d = (DocumentImpl)values[idx]; dlock = d.getUpdateLock(); // if (checkExisting && dlock.hasLock(thread)) // continue; if(exclusive) dlock.acquire(Lock.WRITE_LOCK); else dlock.acquire(Lock.READ_LOCK); } } public void unlock(boolean exclusive) { DocumentImpl d; Lock dlock; final Thread thread = Thread.currentThread(); for(int idx = 0; idx < tabSize; idx++) { if(values[idx] == null || values[idx] == REMOVED) continue; d = (DocumentImpl)values[idx]; dlock = d.getUpdateLock(); if(exclusive) dlock.release(Lock.WRITE_LOCK); else if (dlock.isLockedForRead(thread)) dlock.release(Lock.READ_LOCK); } } public String toString() { StringBuilder result = new StringBuilder(); for( int i=0; i< getDocumentCount(); i++ ) { result.append(getDocumentAt(i)); result.append(", "); } return result.toString(); } }