1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.jrtfs; 27 28 import java.nio.file.DirectoryStream; 29 import java.nio.file.ClosedDirectoryStreamException; 30 import java.nio.file.DirectoryIteratorException; 31 import java.nio.file.NotDirectoryException; 32 import java.nio.file.Path; 33 import java.util.Iterator; 34 import java.util.NoSuchElementException; 35 import java.io.IOException; 36 37 final class JrtDirectoryStream implements DirectoryStream<Path> { 38 private final JrtFileSystem jrtfs; 39 private final byte[] path; 40 // prefix to be used for children of this directory 41 // so that child path are reported relatively (if needed) 42 private final String childPrefix; 43 private final DirectoryStream.Filter<? super Path> filter; 44 private volatile boolean isClosed; 45 private volatile Iterator<Path> itr; 46 47 JrtDirectoryStream(JrtPath jrtPath, 48 DirectoryStream.Filter<? super java.nio.file.Path> filter) 49 throws IOException 50 { 51 this.jrtfs = jrtPath.getFileSystem(); 52 this.path = jrtPath.getResolvedPath(); 53 // sanity check 54 if (!jrtfs.isDirectory(path, true)) 55 throw new NotDirectoryException(jrtPath.toString()); 56 57 // absolute path and does not have funky chars in front like /./java.base 58 if (jrtPath.isAbsolute() && (path.length == jrtPath.getPathLength())) { 59 childPrefix = null; 60 } else { 61 // cases where directory content needs to modified with prefix 62 // like ./java.base, /./java.base, java.base and so on. 63 String dirName = jrtPath.toString(); 64 int idx = dirName.indexOf(JrtFileSystem.getString(path).substring(1)); 65 childPrefix = dirName.substring(0, idx); 66 } 67 this.filter = filter; 68 } 69 70 @Override 71 public synchronized Iterator<Path> iterator() { 72 if (isClosed) 73 throw new ClosedDirectoryStreamException(); 74 if (itr != null) 75 throw new IllegalStateException("Iterator has already been returned"); 76 77 try { 78 itr = jrtfs.iteratorOf(path, childPrefix); 79 } catch (IOException e) { 80 throw new IllegalStateException(e); 81 } 82 return new Iterator<Path>() { 83 /* 84 * next Path value to return from this iterator. 85 * null value means hasNext() not called yet 86 * or last hasNext() returned false or resulted 87 * in exception. If last hasNext() returned true, 88 * then this field has non-null value. 89 */ 90 private Path next; 91 92 // get-and-clear and set-next by these methods 93 private Path getAndClearNext() { 94 assert next != null; 95 Path result = this.next; 96 this.next = null; 97 return result; 98 } 99 100 private void setNext(Path path) { 101 assert path != null; 102 this.next = path; 103 } 104 105 // if hasNext() returns true, 'next' field has non-null Path 106 @Override 107 public synchronized boolean hasNext() { 108 if (next != null) { 109 return true; 110 } 111 112 if (isClosed) { 113 return false; 114 } 115 116 if (filter == null) { 117 if (itr.hasNext()) { 118 setNext(itr.next()); 119 return true; 120 } else { 121 return false; 122 } 123 } else { 124 while (itr.hasNext()) { 125 Path tmpPath = itr.next(); 126 try { 127 if (filter.accept(tmpPath)) { 128 setNext(tmpPath); 129 return true; 130 } 131 } catch (IOException ioe) { 132 throw new DirectoryIteratorException(ioe); 133 } 134 } 135 136 return false; 137 } 138 } 139 140 @Override 141 public synchronized Path next() { 142 if (next != null) { 143 return getAndClearNext(); 144 } 145 146 if (isClosed) { 147 throw new NoSuchElementException(); 148 } 149 150 if (next == null && itr.hasNext()) { 151 // missing hasNext() between next() calls. 152 if (hasNext()) { 153 return getAndClearNext(); 154 } 155 } 156 157 throw new NoSuchElementException(); 158 } 159 160 @Override 161 public void remove() { 162 throw new UnsupportedOperationException(); 163 } 164 }; 165 } 166 167 @Override 168 public synchronized void close() throws IOException { 169 isClosed = true; 170 } 171 }