001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.ecm.core.api; 023 024import java.util.Iterator; 025import java.util.LinkedList; 026import java.util.NoSuchElementException; 027import java.util.Queue; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031 032/** 033 * An iterator over a tree of documents 034 * <p> 035 * The tree is traversed from top to bottom and left to right. 036 * <p> 037 * TODO: move this in an utility package 038 * 039 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 040 */ 041public class DocumentTreeIterator implements Iterator<DocumentModel> { 042 043 private static final Log log = LogFactory.getLog(DocumentTreeIterator.class); 044 045 /** 046 * The document manager session. 047 */ 048 protected final CoreSession session; 049 050 /** 051 * Root document. 052 */ 053 protected final DocumentModel root; 054 055 /** 056 * The current sequence. 057 */ 058 protected Iterator<DocumentModel> sequence; 059 060 /** 061 * The sequence queue. 062 */ 063 protected final Queue<Iterator<DocumentModel>> queue = new LinkedList<Iterator<DocumentModel>>(); 064 065 /** 066 * Creates the iterator given the tree root. 067 */ 068 public DocumentTreeIterator(CoreSession session, DocumentModel root) { 069 this(session, root, false); 070 } 071 072 public DocumentTreeIterator(CoreSession session, DocumentModel root, boolean excludeRoot) { 073 this.root = root; 074 this.session = session; 075 if (excludeRoot) { 076 sequence = session.getChildrenIterator(root.getRef(), null, null, null); 077 } else { 078 sequence = new OneDocSequence(root); 079 } 080 } 081 082 /** 083 * Gets next non empty sequence from queue. 084 * <p> 085 * This will remove from the queue all traversed sequences (the empty ones and the first not empty sequence found). 086 * 087 * @return the first non empty sequence or null if no one was found 088 */ 089 protected Iterator<DocumentModel> getNextNonEmptySequence() { 090 while (true) { 091 Iterator<DocumentModel> seq = queue.poll(); 092 if (seq == null) { 093 return null; 094 } else if (seq.hasNext()) { 095 return seq; 096 } 097 } 098 } 099 100 @Override 101 public boolean hasNext() { 102 if (sequence == null || !sequence.hasNext()) { 103 // no current valid sequence 104 sequence = getNextNonEmptySequence(); 105 if (sequence == null) { 106 return false; 107 } 108 } 109 // we have a sequence to iterate over 110 return true; 111 } 112 113 @Override 114 public DocumentModel next() { 115 // satisfy iterator contract - throw an exception if no more elements to 116 // iterate 117 if (!hasNext()) { 118 throw new NoSuchElementException("no more documents to iterate over"); 119 } 120 // we have a non empty sequence to iterate over 121 DocumentModel doc = sequence.next(); 122 if (doc.isFolder()) { 123 // TODO: load children after the document was traversed 124 // update the sequence queue with children from this folder 125 queue.add(session.getChildrenIterator(doc.getRef(), null, null, null)); 126 } 127 return doc; 128 } 129 130 @Override 131 public void remove() { 132 throw new UnsupportedOperationException("remove is not yet supported"); 133 } 134 135 /** 136 * A sequence of a single doc. 137 * 138 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 139 */ 140 static class OneDocSequence implements Iterator<DocumentModel> { 141 final DocumentModel doc; 142 143 boolean hasNext = true; 144 145 OneDocSequence(DocumentModel doc) { 146 this.doc = doc; 147 } 148 149 @Override 150 public boolean hasNext() { 151 return hasNext; 152 } 153 154 @Override 155 public DocumentModel next() { 156 if (doc == null) { 157 throw new NoSuchElementException("no more documents to iterate over"); 158 } 159 hasNext = false; 160 return doc; 161 } 162 163 @Override 164 public void remove() { 165 throw new UnsupportedOperationException("remove is not yet supported"); 166 } 167 } 168 169 /** 170 * Resets the iterator back to the tree root and clear any cached data. 171 */ 172 public void reset() { 173 sequence = new OneDocSequence(root); 174 queue.clear(); 175 } 176 177}