WindowsDirectoryStream.java 7.6 KB
Newer Older
1
/*
O
ohair 已提交
2
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3 4 5 6
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * 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 12 13 14 15 16 17 18 19 20
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
21 22 23
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
24 25 26 27 28
 */

package sun.nio.fs;

import java.nio.file.*;
29
import java.nio.file.attribute.BasicFileAttributes;
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.IOException;

import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;

/**
 * Windows implementation of DirectoryStream
 */

class WindowsDirectoryStream
    implements DirectoryStream<Path>
{
    private final WindowsPath dir;
    private final DirectoryStream.Filter<? super Path> filter;

    // handle to directory
    private final long handle;
    // first entry in the directory
    private final String firstName;

52 53 54
    // buffer for WIN32_FIND_DATA structure that receives information about file
    private final NativeBuffer findDataBuffer;

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    private final Object closeLock = new Object();

    // need closeLock to access these
    private boolean isOpen = true;
    private Iterator<Path> iterator;


    WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter)
        throws IOException
    {
        this.dir = dir;
        this.filter = filter;

        try {
            // Need to append * or \* to match entries in directory.
            String search = dir.getPathForWin32Calls();
            char last = search.charAt(search.length() -1);
            if (last == ':' || last == '\\') {
                search += "*";
            } else {
                search += "\\*";
            }

            FirstFile first = FindFirstFile(search);
            this.handle = first.handle();
            this.firstName = first.name();
81
            this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
        } catch (WindowsException x) {
            if (x.lastError() == ERROR_DIRECTORY) {
                throw new NotDirectoryException(dir.getPathForExceptionMessage());
            }
            x.rethrowAsIOException(dir);

            // keep compiler happy
            throw new AssertionError();
        }
    }

    @Override
    public void close()
        throws IOException
    {
        synchronized (closeLock) {
            if (!isOpen)
                return;
            isOpen = false;
        }
102
        findDataBuffer.release();
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
        try {
            FindClose(handle);
        } catch (WindowsException x) {
            x.rethrowAsIOException(dir);
        }
    }

    @Override
    public Iterator<Path> iterator() {
        if (!isOpen) {
            throw new IllegalStateException("Directory stream is closed");
        }
        synchronized (this) {
            if (iterator != null)
                throw new IllegalStateException("Iterator already obtained");
            iterator = new WindowsDirectoryIterator(firstName);
            return iterator;
        }
    }

    private class WindowsDirectoryIterator implements Iterator<Path> {
        private boolean atEof;
        private String first;
        private Path nextEntry;
127
        private String prefix;
128 129 130 131

        WindowsDirectoryIterator(String first) {
            atEof = false;
            this.first = first;
132 133 134 135 136 137 138 139 140 141
            if (dir.needsSlashWhenResolving()) {
                prefix = dir.toString() + "\\";
            } else {
                prefix = dir.toString();
            }
        }

        // links to self and parent directories are ignored
        private boolean isSelfOrParent(String name) {
            return name.equals(".") || name.equals("..");
142 143 144
        }

        // applies filter and also ignores "." and ".."
145
        private Path acceptEntry(String s, BasicFileAttributes attrs) {
146
            Path entry = WindowsPath
147
                .createFromNormalizedPath(dir.getFileSystem(), prefix + s, attrs);
148 149 150 151
            try {
                if (filter.accept(entry))
                    return entry;
            } catch (IOException ioe) {
152
                throw new DirectoryIteratorException(ioe);
153
            }
154
            return null;
155 156 157 158 159 160
        }

        // reads next directory entry
        private Path readNextEntry() {
            // handle first element returned by search
            if (first != null) {
161
                nextEntry = isSelfOrParent(first) ? null : acceptEntry(first, null);
162 163 164 165 166 167
                first = null;
                if (nextEntry != null)
                    return nextEntry;
            }

            for (;;) {
168 169 170
                String name = null;
                WindowsFileAttributes attrs;

171 172 173
                // synchronize on closeLock to prevent close while reading
                synchronized (closeLock) {
                    try {
174 175
                        if (isOpen) {
                            name = FindNextFile(handle, findDataBuffer.address());
176
                        }
177
                    } catch (WindowsException x) {
178 179 180 181 182 183 184 185
                        IOException ioe = x.asIOException(dir);
                        throw new DirectoryIteratorException(ioe);
                    }

                    // NO_MORE_FILES or stream closed
                    if (name == null) {
                        atEof = true;
                        return null;
186 187
                    }

188 189 190 191
                    // ignore link to self and parent directories
                    if (isSelfOrParent(name))
                        continue;

192 193 194 195 196 197
                    // grab the attributes from the WIN32_FIND_DATA structure
                    // (needs to be done while holding closeLock because close
                    // will release the buffer)
                    attrs = WindowsFileAttributes
                        .fromFindData(findDataBuffer.address());
                }
198

199 200
                // return entry if accepted by filter
                Path entry = acceptEntry(name, attrs);
201 202 203 204 205 206 207
                if (entry != null)
                    return entry;
            }
        }

        @Override
        public synchronized boolean hasNext() {
208
            if (nextEntry == null && !atEof)
209 210 211 212 213 214
                nextEntry = readNextEntry();
            return nextEntry != null;
        }

        @Override
        public synchronized Path next() {
215 216 217 218 219 220
            Path result = null;
            if (nextEntry == null && !atEof) {
                result = readNextEntry();
            } else {
                result = nextEntry;
                nextEntry = null;
221
            }
222 223 224
            if (result == null)
                throw new NoSuchElementException();
            return result;
225 226 227 228
        }

        @Override
        public void remove() {
229
            throw new UnsupportedOperationException();
230 231 232
        }
    }
}