001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 2004-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openidex.search;
043:
044: import java.util.ArrayList;
045: import java.util.Enumeration;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.NoSuchElementException;
049: import org.openide.filesystems.FileObject;
050: import org.openide.loaders.DataFolder;
051: import org.openide.loaders.DataObject;
052:
053: /**
054: *
055: * @author Marian Petras
056: */
057: class SimpleSearchIterator implements Iterator<DataObject> {
058:
059: /** current enumeration of children */
060: private Enumeration<DataObject> childrenEnum;
061: /**
062: * filters to be applied on the current enumeration of children
063: * ({@link #childrenEnum})
064: */
065: private List<FileObjectFilter> filters;
066: /**
067: * contains either an equal copy of {@link #filters} or <code>null</code>
068: */
069: private List<FileObjectFilter> filtersCopy;
070: /** */
071: private final boolean recursive;
072: /** stack of the ancestor folders' children enumerations */
073: private final List<Enumeration<DataObject>> enums = new ArrayList<Enumeration<DataObject>>(); //unsynced stack
074: /**
075: * stack of filter lists to be applied on children of the ancestor folders
076: * ({@link #enums})
077: */
078: private final List<List<FileObjectFilter>> filterLists = new ArrayList<List<FileObjectFilter>>(); //unsynced stack
079: /** whether value of {@link #nextObject} is up-to-date */
080: private boolean upToDate = false;
081: /**
082: * <code>DataObject</code> to be returned the next time method
083: * {@link #next()} is called
084: */
085: private DataObject nextObject;
086:
087: /**
088: */
089: SimpleSearchIterator(DataFolder folder, boolean recursive,
090: List<FileObjectFilter> filters) {
091: this .childrenEnum = folder.children(false);
092: this .recursive = recursive;
093: this .filters = (filters != null) ? new ArrayList<FileObjectFilter>(
094: filters)
095: : null;
096: }
097:
098: /**
099: */
100: public boolean hasNext() {
101: if (!upToDate) {
102: update();
103: }
104: return nextObject != null;
105: }
106:
107: /**
108: */
109: public DataObject next() {
110: if (!hasNext()) {
111: throw new NoSuchElementException();
112: }
113:
114: upToDate = false;
115: return nextObject;
116: }
117:
118: /**
119: */
120: private void update() {
121: assert upToDate == false;
122: assert childrenEnum != null;
123: do {
124: if (childrenEnum.hasMoreElements()) {
125: DataObject dataObject = childrenEnum.nextElement();
126: FileObject file = dataObject.getPrimaryFile();
127: if (file.isFolder()) {
128: if (!recursive) {
129: continue;
130: }
131:
132: if (filters != null) {
133: final List<FileObjectFilter> subfolderFilters = checkFolderFilters(file);
134: if (subfolderFilters == null) {
135: continue;
136: }
137:
138: filterLists.add(filters);
139: if (subfolderFilters.size() != filters.size()) {
140: filters = (!subfolderFilters.isEmpty()) ? subfolderFilters
141: : null;
142: }
143: } else {
144: filterLists.add(null);
145: }
146: enums.add(childrenEnum);
147: childrenEnum = ((DataFolder) dataObject)
148: .children(false);
149: } else {
150: if ((filters != null) && !checkFileFilters(file)) {
151: continue;
152: }
153:
154: nextObject = dataObject;
155: break;
156: }
157: } else {
158: assert enums.isEmpty() == filterLists.isEmpty();
159:
160: nextObject = null;
161:
162: if (enums.isEmpty()) {
163: childrenEnum = null;
164: continue;
165: }
166:
167: /* pop an element from the stack of children enumerations: */
168: childrenEnum = enums.remove(enums.size() - 1);
169:
170: /* pop an element from the stack of FileObjectFilters: */
171: filters = filterLists.remove(filterLists.size() - 1);
172: if ((filtersCopy != null)
173: && (filtersCopy.size() != filters.size())) {
174: filtersCopy = null;
175: }
176: }
177: } while (childrenEnum != null);
178:
179: upToDate = true;
180: }
181:
182: /**
183: * Computes a list of filters to be applied on the folder's children.
184: * The current list of filters is used as a base and then each filter
185: * is checked with the folder as a parameter.
186: * <p>
187: * If any of the filters returns <code>DO_NOT_TRAVERSE</code>,
188: * <code>the method returns <code>null</code> and no further filters
189: * are checked.
190: * If a filter returns <code>TRAVERSE_ALL_SUBFOLDERS</code>,
191: * the filter is removed from the base as it needs not be applied
192: * on the folder's children. The remaining list of filters is returned
193: * as a result.
194: *
195: * @param folder folder to compute children filters for
196: * @return list of filters to be applied on the folder's children;
197: * or <code>null</code> if the folder should not be traversed
198: */
199: private List<FileObjectFilter> checkFolderFilters(
200: final FileObject folder) {
201: assert folder.isFolder();
202: assert filters != null;
203:
204: if (filtersCopy == null) {
205: filtersCopy = new ArrayList<FileObjectFilter>(filters);
206: }
207:
208: List<FileObjectFilter> result = filtersCopy;
209: cycle: for (Iterator<FileObjectFilter> i = result.iterator(); i
210: .hasNext();) {
211: FileObjectFilter filter = i.next();
212: final int traverseCommand = filter.traverseFolder(folder);
213: switch (traverseCommand) {
214: case FileObjectFilter.TRAVERSE:
215: break;
216: case FileObjectFilter.DO_NOT_TRAVERSE:
217: result = null;
218: break cycle;
219: case FileObjectFilter.TRAVERSE_ALL_SUBFOLDERS:
220: i.remove();
221: filtersCopy = null;
222: break;
223: default:
224: assert false;
225: break;
226: }
227: }
228:
229: return result;
230: }
231:
232: /**
233: * Checks whether the file passes all of the current
234: * {@link #filters}.
235: *
236: * @param file file to be checked
237: * @return <code>true</code> if the file passed all of the filters,
238: * <code>false</code> otherwise
239: */
240: private boolean checkFileFilters(FileObject file) {
241: assert file.isFolder() == false;
242: assert filters != null;
243:
244: for (FileObjectFilter filter : filters) {
245: if (!filter.searchFile(file)) {
246: return false;
247: }
248: }
249:
250: return true;
251: }
252:
253: /**
254: */
255: public void remove() {
256: throw new UnsupportedOperationException();
257: }
258:
259: }
|