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 sun.nio.fs; 27 28 import java.nio.file.*; 29 import static java.nio.file.StandardOpenOption.*; 30 import java.nio.file.attribute.*; 31 import java.nio.channels.*; 32 import java.nio.ByteBuffer; 33 import java.io.*; 34 import java.util.*; 35 36 /** 37 * Base implementation class for a {@code Path}. 38 */ 39 40 abstract class AbstractPath extends Path { 41 protected AbstractPath() { } 42 43 @Override 44 public final Path createFile(FileAttribute<?>... attrs) 45 throws IOException 46 { 47 EnumSet<StandardOpenOption> options = EnumSet.of(CREATE_NEW, WRITE); 48 SeekableByteChannel sbc = newByteChannel(options, attrs); 49 try { 50 sbc.close(); 51 } catch (IOException x) { 52 // ignore 53 } 54 return this; 55 } 56 57 /** 58 * Deletes a file. The {@code failIfNotExists} parameters determines if an 59 * {@code IOException} is thrown when the file does not exist. 60 */ 61 abstract void implDelete(boolean failIfNotExists) throws IOException; 62 63 @Override 64 public final void delete() throws IOException { 65 implDelete(true); 66 } 67 68 @Override 69 public final void deleteIfExists() throws IOException { 70 implDelete(false); 71 } 72 73 @Override 74 public final InputStream newInputStream(OpenOption... options) 75 throws IOException 76 { 77 if (options.length > 0) { 78 for (OpenOption opt: options) { 79 if (opt != READ) 80 throw new UnsupportedOperationException("'" + opt + "' not allowed"); 81 } 82 } 83 return Channels.newInputStream(newByteChannel()); 84 } 85 86 @Override 87 public final OutputStream newOutputStream(OpenOption... options) 88 throws IOException 89 { 90 int len = options.length; 91 Set<OpenOption> opts = new HashSet<OpenOption>(len + 3); 92 if (len == 0) { 93 opts.add(CREATE); 94 opts.add(TRUNCATE_EXISTING); 95 } else { 96 for (OpenOption opt: options) { 97 if (opt == READ) 98 throw new IllegalArgumentException("READ not allowed"); 99 opts.add(opt); 100 } 101 } 102 opts.add(WRITE); 103 return Channels.newOutputStream(newByteChannel(opts)); 104 } 105 106 @Override 107 public final SeekableByteChannel newByteChannel(OpenOption... options) 108 throws IOException 109 { 110 Set<OpenOption> set = new HashSet<OpenOption>(options.length); 111 Collections.addAll(set, options); 112 return newByteChannel(set); 113 } 114 115 private static final DirectoryStream.Filter<Path> acceptAllFilter = 116 new DirectoryStream.Filter<Path>() { 117 @Override public boolean accept(Path entry) { return true; } 118 }; 119 120 @Override 121 public final DirectoryStream<Path> newDirectoryStream() throws IOException { 122 return newDirectoryStream(acceptAllFilter); 123 } 124 125 @Override 126 public final DirectoryStream<Path> newDirectoryStream(String glob) 127 throws IOException 128 { 129 // avoid creating a matcher if all entries are required. 130 if (glob.equals("*")) 131 return newDirectoryStream(); 132 133 // create a matcher and return a filter that uses it. 134 final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob); 135 DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { 136 @Override 137 public boolean accept(Path entry) { 138 return matcher.matches(entry.getName()); 139 } 140 }; 141 return newDirectoryStream(filter); 142 } 143 144 @Override 145 public final boolean exists() { 146 try { 147 checkAccess(); 148 return true; 149 } catch (IOException x) { 150 // unable to determine if file exists 151 } 152 return false; 153 } 154 155 @Override 156 public final boolean notExists() { 157 try { 158 checkAccess(); 159 return false; 160 } catch (NoSuchFileException x) { 161 // file confirmed not to exist 162 return true; 163 } catch (IOException x) { 164 return false; 165 } 166 } 167 168 private static final WatchEvent.Modifier[] NO_MODIFIERS = new WatchEvent.Modifier[0]; 169 170 @Override 171 public final WatchKey register(WatchService watcher, 172 WatchEvent.Kind<?>... events) 173 throws IOException 174 { 175 return register(watcher, events, NO_MODIFIERS); 176 } 177 178 abstract void implCopyTo(Path target, CopyOption... options) 179 throws IOException; 180 181 @Override 182 public final Path copyTo(Path target, CopyOption... options) 183 throws IOException 184 { 185 if ((getFileSystem().provider() == target.getFileSystem().provider())) { 186 implCopyTo(target, options); 187 } else { 188 copyToForeignTarget(target, options); 189 } 190 return target; 191 } 192 193 abstract void implMoveTo(Path target, CopyOption... options) 194 throws IOException; 195 196 @Override 197 public final Path moveTo(Path target, CopyOption... options) 198 throws IOException 199 { 200 if ((getFileSystem().provider() == target.getFileSystem().provider())) { 201 implMoveTo(target, options); 202 } else { 203 // different providers so copy + delete 204 copyToForeignTarget(target, convertMoveToCopyOptions(options)); 205 delete(); 206 } 207 return target; 208 } 209 210 /** 211 * Converts the given array of options for moving a file to options suitable 212 * for copying the file when a move is implemented as copy + delete. 213 */ 214 private static CopyOption[] convertMoveToCopyOptions(CopyOption... options) 215 throws AtomicMoveNotSupportedException 216 { 217 int len = options.length; 218 CopyOption[] newOptions = new CopyOption[len+2]; 219 for (int i=0; i<len; i++) { 220 CopyOption option = options[i]; 221 if (option == StandardCopyOption.ATOMIC_MOVE) { 222 throw new AtomicMoveNotSupportedException(null, null, 223 "Atomic move between providers is not supported"); 224 } 225 newOptions[i] = option; 226 } 227 newOptions[len] = LinkOption.NOFOLLOW_LINKS; 228 newOptions[len+1] = StandardCopyOption.COPY_ATTRIBUTES; 229 return newOptions; 230 } 231 232 /** 233 * Parses the arguments for a file copy operation. 234 */ 235 private static class CopyOptions { 236 boolean replaceExisting = false; 237 boolean copyAttributes = false; 238 boolean followLinks = true; 239 240 private CopyOptions() { } 241 242 static CopyOptions parse(CopyOption... options) { 243 CopyOptions result = new CopyOptions(); 244 for (CopyOption option: options) { 245 if (option == StandardCopyOption.REPLACE_EXISTING) { 246 result.replaceExisting = true; 247 continue; 248 } 249 if (option == LinkOption.NOFOLLOW_LINKS) { 250 result.followLinks = false; 251 continue; 252 } 253 if (option == StandardCopyOption.COPY_ATTRIBUTES) { 254 result.copyAttributes = true; 255 continue; 256 } 257 if (option == null) 258 throw new NullPointerException(); 259 throw new UnsupportedOperationException("'" + option + 260 "' is not a recognized copy option"); 261 } 262 return result; 263 } 264 } 265 266 /** 267 * Simple cross-provider copy where the target is a Path. 268 */ 269 private void copyToForeignTarget(Path target, CopyOption... options) 270 throws IOException 271 { 272 CopyOptions opts = CopyOptions.parse(options); 273 LinkOption[] linkOptions = (opts.followLinks) ? new LinkOption[0] : 274 new LinkOption[] { LinkOption.NOFOLLOW_LINKS }; 275 276 // attributes of source file 277 BasicFileAttributes attrs = Attributes 278 .readBasicFileAttributes(this, linkOptions); 279 if (attrs.isSymbolicLink()) 280 throw new IOException("Copying of symbolic links not supported"); 281 282 // check if target exists 283 boolean exists; 284 if (opts.replaceExisting) { 285 try { 286 target.deleteIfExists(); 287 exists = false; 288 } catch (DirectoryNotEmptyException x) { 289 // let exception translate to FileAlreadyExistsException (6895012) 290 exists = true; 291 } 292 } else { 293 exists = target.exists(); 294 } 295 if (exists) 296 throw new FileAlreadyExistsException(target.toString()); 297 298 // create directory or file 299 if (attrs.isDirectory()) { 300 target.createDirectory(); 301 } else { 302 copyRegularFileToForeignTarget(target); 303 } 304 305 // copy basic attributes to target 306 if (opts.copyAttributes) { 307 BasicFileAttributeView view = target 308 .getFileAttributeView(BasicFileAttributeView.class, linkOptions); 309 try { 310 view.setTimes(attrs.lastModifiedTime(), 311 attrs.lastAccessTime(), 312 attrs.creationTime()); 313 } catch (IOException x) { 314 // rollback 315 try { 316 target.delete(); 317 } catch (IOException ignore) { } 318 throw x; 319 } 320 } 321 } 322 323 324 /** 325 * Simple copy of regular file to a target file that exists. 326 */ 327 private void copyRegularFileToForeignTarget(Path target) 328 throws IOException 329 { 330 ReadableByteChannel rbc = newByteChannel(); 331 try { 332 // open target file for writing 333 SeekableByteChannel sbc = target.newByteChannel(CREATE_NEW, WRITE); 334 335 // simple copy loop 336 try { 337 ByteBuffer buf = ByteBuffer.wrap(new byte[8192]); 338 int n = 0; 339 for (;;) { 340 n = rbc.read(buf); 341 if (n < 0) 342 break; 343 assert n > 0; 344 buf.flip(); 345 while (buf.hasRemaining()) { 346 sbc.write(buf); 347 } 348 buf.rewind(); 349 } 350 351 } finally { 352 sbc.close(); 353 } 354 } finally { 355 rbc.close(); 356 } 357 } 358 359 /** 360 * Splits the given attribute name into the name of an attribute view and 361 * the attribute. If the attribute view is not identified then it assumed 362 * to be "basic". 363 */ 364 private static String[] split(String attribute) { 365 String[] s = new String[2]; 366 int pos = attribute.indexOf(':'); 367 if (pos == -1) { 368 s[0] = "basic"; 369 s[1] = attribute; 370 } else { 371 s[0] = attribute.substring(0, pos++); 372 s[1] = (pos == attribute.length()) ? "" : attribute.substring(pos); 373 } 374 return s; 375 } 376 377 /** 378 * Gets a DynamicFileAttributeView by name. Returns {@code null} if the 379 * view is not available. 380 */ 381 abstract DynamicFileAttributeView getFileAttributeView(String name, 382 LinkOption... options); 383 384 @Override 385 public final void setAttribute(String attribute, 386 Object value, 387 LinkOption... options) 388 throws IOException 389 { 390 String[] s = split(attribute); 391 DynamicFileAttributeView view = getFileAttributeView(s[0], options); 392 if (view == null) 393 throw new UnsupportedOperationException("View '" + s[0] + "' not available"); 394 view.setAttribute(s[1], value); 395 } 396 397 @Override 398 public final Object getAttribute(String attribute, LinkOption... options) 399 throws IOException 400 { 401 String[] s = split(attribute); 402 DynamicFileAttributeView view = getFileAttributeView(s[0], options); 403 return (view == null) ? null : view.getAttribute(s[1]); 404 } 405 406 @Override 407 public final Map<String,?> readAttributes(String attributes, LinkOption... options) 408 throws IOException 409 { 410 String[] s = split(attributes); 411 DynamicFileAttributeView view = getFileAttributeView(s[0], options); 412 if (view == null) 413 return Collections.emptyMap(); 414 return view.readAttributes(s[1].split(",")); 415 } 416 } | 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 sun.nio.fs; 27 28 import java.nio.file.*; 29 import java.io.File; 30 import java.io.IOException; 31 import java.util.Iterator; 32 import java.util.NoSuchElementException; 33 import java.util.Objects; 34 35 /** 36 * Base implementation class of {@code Path}. 37 */ 38 39 abstract class AbstractPath implements Path { 40 protected AbstractPath() { } 41 42 @Override 43 public final boolean startsWith(String other) { 44 return startsWith(getFileSystem().getPath(other)); 45 } 46 47 @Override 48 public final boolean endsWith(String other) { 49 return endsWith(getFileSystem().getPath(other)); 50 } 51 52 @Override 53 public final Path resolve(String other) { 54 return resolve(getFileSystem().getPath(other)); 55 } 56 57 @Override 58 public final Path resolveSibling(Path other) { 59 Path parent = getParent(); 60 return (parent == null) ? Objects.nonNull(other) : parent.resolve(other); 61 } 62 63 @Override 64 public final Path resolveSibling(String other) { 65 return resolveSibling(getFileSystem().getPath(other)); 66 } 67 68 @Override 69 public final Iterator<Path> iterator() { 70 return new Iterator<Path>() { 71 private int i = 0; 72 @Override 73 public boolean hasNext() { 74 return (i < getNameCount()); 75 } 76 @Override 77 public Path next() { 78 if (i < getNameCount()) { 79 Path result = getName(i); 80 i++; 81 return result; 82 } else { 83 throw new NoSuchElementException(); 84 } 85 } 86 @Override 87 public void remove() { 88 throw new UnsupportedOperationException(); 89 } 90 }; 91 } 92 93 @Override 94 public final File toFile() { 95 return new File(toString()); 96 } 97 98 @Override 99 public final WatchKey register(WatchService watcher, 100 WatchEvent.Kind<?>... events) 101 throws IOException 102 { 103 return register(watcher, events, new WatchEvent.Modifier[0]); 104 } 105 } |