1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.jimfs; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; 21 import static java.nio.file.StandardOpenOption.APPEND; 22 import static java.nio.file.StandardOpenOption.CREATE; 23 import static java.nio.file.StandardOpenOption.READ; 24 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; 25 import static java.nio.file.StandardOpenOption.WRITE; 26 27 import com.google.common.collect.ImmutableSet; 28 import com.google.common.collect.Lists; 29 import java.nio.file.CopyOption; 30 import java.nio.file.LinkOption; 31 import java.nio.file.OpenOption; 32 import java.util.Arrays; 33 import java.util.Collection; 34 import java.util.Set; 35 36 /** 37 * Utility methods for normalizing user-provided options arrays and sets to canonical immutable sets 38 * of options. 39 * 40 * @author Colin Decker 41 */ 42 final class Options { 43 Options()44 private Options() {} 45 46 /** Immutable set containing LinkOption.NOFOLLOW_LINKS. */ 47 public static final ImmutableSet<LinkOption> NOFOLLOW_LINKS = 48 ImmutableSet.of(LinkOption.NOFOLLOW_LINKS); 49 50 /** Immutable empty LinkOption set. */ 51 public static final ImmutableSet<LinkOption> FOLLOW_LINKS = ImmutableSet.of(); 52 53 private static final ImmutableSet<OpenOption> DEFAULT_READ = ImmutableSet.<OpenOption>of(READ); 54 55 private static final ImmutableSet<OpenOption> DEFAULT_READ_NOFOLLOW_LINKS = 56 ImmutableSet.<OpenOption>of(READ, LinkOption.NOFOLLOW_LINKS); 57 58 private static final ImmutableSet<OpenOption> DEFAULT_WRITE = 59 ImmutableSet.<OpenOption>of(WRITE, CREATE, TRUNCATE_EXISTING); 60 61 /** Returns an immutable set of link options. */ getLinkOptions(LinkOption... options)62 public static ImmutableSet<LinkOption> getLinkOptions(LinkOption... options) { 63 return options.length == 0 ? FOLLOW_LINKS : NOFOLLOW_LINKS; 64 } 65 66 /** Returns an immutable set of open options for opening a new file channel. */ getOptionsForChannel(Set<? extends OpenOption> options)67 public static ImmutableSet<OpenOption> getOptionsForChannel(Set<? extends OpenOption> options) { 68 if (options.isEmpty()) { 69 return DEFAULT_READ; 70 } 71 72 boolean append = options.contains(APPEND); 73 boolean write = append || options.contains(WRITE); 74 boolean read = !write || options.contains(READ); 75 76 if (read) { 77 if (append) { 78 throw new UnsupportedOperationException("'READ' + 'APPEND' not allowed"); 79 } 80 81 if (!write) { 82 // ignore all write related options 83 return options.contains(LinkOption.NOFOLLOW_LINKS) 84 ? DEFAULT_READ_NOFOLLOW_LINKS 85 : DEFAULT_READ; 86 } 87 } 88 89 // options contains write or append and may also contain read 90 // it does not contain both read and append 91 return addWrite(options); 92 } 93 94 /** Returns an immutable set of open options for opening a new input stream. */ 95 @SuppressWarnings("unchecked") // safe covariant cast getOptionsForInputStream(OpenOption... options)96 public static ImmutableSet<OpenOption> getOptionsForInputStream(OpenOption... options) { 97 boolean nofollowLinks = false; 98 for (OpenOption option : options) { 99 if (checkNotNull(option) != READ) { 100 if (option == LinkOption.NOFOLLOW_LINKS) { 101 nofollowLinks = true; 102 } else { 103 throw new UnsupportedOperationException("'" + option + "' not allowed"); 104 } 105 } 106 } 107 108 // just return the link options for finding the file, nothing else is needed 109 return (ImmutableSet<OpenOption>) 110 (ImmutableSet<?>) (nofollowLinks ? NOFOLLOW_LINKS : FOLLOW_LINKS); 111 } 112 113 /** Returns an immutable set of open options for opening a new output stream. */ getOptionsForOutputStream(OpenOption... options)114 public static ImmutableSet<OpenOption> getOptionsForOutputStream(OpenOption... options) { 115 if (options.length == 0) { 116 return DEFAULT_WRITE; 117 } 118 119 ImmutableSet<OpenOption> result = addWrite(Arrays.asList(options)); 120 if (result.contains(READ)) { 121 throw new UnsupportedOperationException("'READ' not allowed"); 122 } 123 return result; 124 } 125 126 /** 127 * Returns an {@link ImmutableSet} copy of the given {@code options}, adding {@link 128 * StandardOpenOption#WRITE} if it isn't already present. 129 */ addWrite(Collection<? extends OpenOption> options)130 private static ImmutableSet<OpenOption> addWrite(Collection<? extends OpenOption> options) { 131 return options.contains(WRITE) 132 ? ImmutableSet.copyOf(options) 133 : ImmutableSet.<OpenOption>builder().add(WRITE).addAll(options).build(); 134 } 135 136 /** Returns an immutable set of the given options for a move. */ getMoveOptions(CopyOption... options)137 public static ImmutableSet<CopyOption> getMoveOptions(CopyOption... options) { 138 return ImmutableSet.copyOf(Lists.asList(LinkOption.NOFOLLOW_LINKS, options)); 139 } 140 141 /** Returns an immutable set of the given options for a copy. */ getCopyOptions(CopyOption... options)142 public static ImmutableSet<CopyOption> getCopyOptions(CopyOption... options) { 143 ImmutableSet<CopyOption> result = ImmutableSet.copyOf(options); 144 if (result.contains(ATOMIC_MOVE)) { 145 throw new UnsupportedOperationException("'ATOMIC_MOVE' not allowed"); 146 } 147 return result; 148 } 149 } 150