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 package jdk.internal.jrtfs; 26 27 import java.nio.file.DirectoryStream; 28 import java.nio.file.ClosedDirectoryStreamException; 29 import java.nio.file.DirectoryIteratorException; 30 import java.nio.file.NotDirectoryException; 31 import java.nio.file.Path; 32 import java.util.Iterator; 33 import java.util.NoSuchElementException; 34 import java.io.IOException; 35 36 /** 37 * DirectoryStream implementation for jrt file system implementations. 38 */ 39 final class JrtDirectoryStream implements DirectoryStream<Path> { 40 41 private final AbstractJrtFileSystem jrtfs; 42 private final byte[] path; 43 // prefix to be used for children of this directory 44 // so that child path are reported relatively (if needed) 45 private final String childPrefix; 46 private final DirectoryStream.Filter<? super Path> filter; 47 private volatile boolean isClosed; 48 private volatile Iterator<Path> itr; 49 50 JrtDirectoryStream(AbstractJrtPath jrtPath, 51 DirectoryStream.Filter<? super java.nio.file.Path> filter) 52 throws IOException { 53 this.jrtfs = jrtPath.getFileSystem(); 54 this.path = jrtPath.getResolvedPath(); 55 // sanity check 56 if (!jrtfs.isDirectory(path, true)) { 57 throw new NotDirectoryException(jrtPath.toString()); 58 } 59 60 // absolute path and does not have funky chars in front like /./java.base 61 if (jrtPath.isAbsolute() && (path.length == jrtPath.getPathLength())) { 62 childPrefix = null; 63 } else { 64 // cases where directory content needs to modified with prefix 65 // like ./java.base, /./java.base, java.base and so on. 66 String dirName = jrtPath.toString(); 67 int idx = dirName.indexOf(JrtFileSystem.getString(path).substring(1)); 68 childPrefix = dirName.substring(0, idx); 69 } 70 this.filter = filter; 71 } 72 73 @Override 74 public synchronized Iterator<Path> iterator() { 75 if (isClosed) { 76 throw new ClosedDirectoryStreamException(); 77 } 78 if (itr != null) { 79 throw new IllegalStateException("Iterator has already been returned"); 80 } 81 82 try { 83 itr = jrtfs.iteratorOf(path, childPrefix); 84 } catch (IOException e) { 85 throw new IllegalStateException(e); 86 } 87 return new Iterator<Path>() { 88 /* 89 * next Path value to return from this iterator. 90 * null value means hasNext() not called yet 91 * or last hasNext() returned false or resulted 92 * in exception. If last hasNext() returned true, 93 * then this field has non-null value. 94 */ 95 private Path next; 96 97 // get-and-clear and set-next by these methods 98 private Path getAndClearNext() { 99 assert next != null; 100 Path result = this.next; 101 this.next = null; 102 return result; 103 } 104 105 private void setNext(Path path) { 106 assert path != null; 107 this.next = path; 108 } 109 110 // if hasNext() returns true, 'next' field has non-null Path 111 @Override 112 public synchronized boolean hasNext() { 113 if (next != null) { 114 return true; 115 } 116 117 if (isClosed) { 118 return false; 119 } 120 121 if (filter == null) { 122 if (itr.hasNext()) { 123 setNext(itr.next()); 124 return true; 125 } else { 126 return false; 127 } 128 } else { 129 while (itr.hasNext()) { 130 Path tmpPath = itr.next(); 131 try { 132 if (filter.accept(tmpPath)) { 133 setNext(tmpPath); 134 return true; 135 } 136 } catch (IOException ioe) { 137 throw new DirectoryIteratorException(ioe); 138 } 139 } 140 141 return false; 142 } 143 } 144 145 @Override 146 public synchronized Path next() { 147 if (next != null) { 148 return getAndClearNext(); 149 } 150 151 if (isClosed) { 152 throw new NoSuchElementException(); 153 } 154 155 if (next == null && itr.hasNext()) { 156 // missing hasNext() between next() calls. 157 if (hasNext()) { 158 return getAndClearNext(); 159 } 160 } 161 162 throw new NoSuchElementException(); 163 } 164 165 @Override 166 public void remove() { 167 throw new UnsupportedOperationException(); 168 } 169 }; 170 } 171 172 @Override 173 public synchronized void close() throws IOException { 174 isClosed = true; 175 } 176 }