001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.io;
019
020import java.io.File;
021import java.io.FileNotFoundException;
022import java.io.IOException;
023import java.io.RandomAccessFile;
024import java.nio.file.OpenOption;
025import java.nio.file.Path;
026import java.nio.file.StandardOpenOption;
027import java.util.Objects;
028
029import org.apache.commons.io.function.IOConsumer;
030import org.apache.commons.io.function.IOFunction;
031
032/**
033 * Enumerates access modes for {@link RandomAccessFile} with factory methods.
034 *
035 * @see RandomAccessFile#RandomAccessFile(File, String)
036 * @see RandomAccessFile#RandomAccessFile(String, String)
037 * @see Enum
038 * @since 2.12.0
039 */
040public enum RandomAccessFileMode {
041
042    /**
043     * Defines mode {@value #R} to open a {@link RandomAccessFile} for reading only.
044     *
045     * @see RandomAccessFile#RandomAccessFile(File, String)
046     * @see RandomAccessFile#RandomAccessFile(String, String)
047     */
048    READ_ONLY(RandomAccessFileMode.R, 1), // NOPMD bug https://github.com/pmd/pmd/issues/5263
049
050    /**
051     * Defines mode {@value #RW} to open a {@link RandomAccessFile} for reading and writing.
052     *
053     * @see RandomAccessFile#RandomAccessFile(File, String)
054     * @see RandomAccessFile#RandomAccessFile(String, String)
055     */
056    READ_WRITE(RandomAccessFileMode.RW, 2), // NOPMD bug https://github.com/pmd/pmd/issues/5263
057
058    /**
059     * Defines mode {@value #RWS} to open a {@link RandomAccessFile} for reading and writing, as with {@value #RW}, and also require that every update to the
060     * file's content or metadata be written synchronously to the underlying storage device.
061     *
062     * @see RandomAccessFile#RandomAccessFile(File, String)
063     * @see RandomAccessFile#RandomAccessFile(String, String)
064     * @see StandardOpenOption#SYNC
065     */
066    READ_WRITE_SYNC_ALL(RandomAccessFileMode.RWS, 4), // NOPMD bug https://github.com/pmd/pmd/issues/5263
067
068    /**
069     * Defines mode {@value #RWD} to open a {@link RandomAccessFile} for reading and writing, as with {@value #RW}, and also require that every update to the
070     * file's content be written synchronously to the underlying storage device.
071     *
072     * @see RandomAccessFile#RandomAccessFile(File, String)
073     * @see RandomAccessFile#RandomAccessFile(String, String)
074     * @see StandardOpenOption#DSYNC
075     */
076    READ_WRITE_SYNC_CONTENT(RandomAccessFileMode.RWD, 3); // NOPMD bug https://github.com/pmd/pmd/issues/5263
077
078    private static final String R = "r";
079    private static final String RW = "rw";
080    private static final String RWD = "rwd";
081    private static final String RWS = "rws";
082
083    /**
084     * Gets the enum value that best fits the given {@link OpenOption}s.
085     * <p>
086     * The input must be a legal and working combination for NIO.
087     * </p>
088     *
089     * @param openOption options like {@link StandardOpenOption}.
090     * @return best fit, by default {@link #READ_ONLY}.
091     * @see StandardOpenOption
092     * @since 2.18.0
093     */
094    public static RandomAccessFileMode valueOf(final OpenOption... openOption) {
095        RandomAccessFileMode bestFit = READ_ONLY;
096        for (final OpenOption option : openOption) {
097            if (option instanceof StandardOpenOption) {
098                switch ((StandardOpenOption) option) {
099                case WRITE:
100                    if (!bestFit.implies(READ_WRITE)) {
101                        bestFit = READ_WRITE;
102                    }
103                    break;
104                case DSYNC:
105                    if (!bestFit.implies(READ_WRITE_SYNC_CONTENT)) {
106                        bestFit = READ_WRITE_SYNC_CONTENT;
107                    }
108                    break;
109                case SYNC:
110                    if (!bestFit.implies(READ_WRITE_SYNC_ALL)) {
111                        bestFit = READ_WRITE_SYNC_ALL;
112                    }
113                    break;
114                default:
115                    // explicit case skip (spotbugs)
116                    continue;
117                }
118            }
119        }
120        return bestFit;
121    }
122
123    /**
124     * Gets the {@link RandomAccessFileMode} value for the given mode, one of {@value #R}, {@value #RW}, {@value #RWD}, or {@value #RWS}.
125     *
126     * @param mode one of {@value #R}, {@value #RW}, {@value #RWD}, or {@value #RWS}.
127     * @return A RandomAccessFileMode.
128     * @throws IllegalArgumentException Thrown when mode is not one of {@value #R}, {@value #RW}, {@value #RWD}, or {@value #RWS}.
129     * @since 2.18.0
130     */
131    public static RandomAccessFileMode valueOfMode(final String mode) {
132        switch (mode) {
133        case R:
134            return READ_ONLY;
135        case RW:
136            return READ_WRITE;
137        case RWD:
138            return READ_WRITE_SYNC_CONTENT;
139        case RWS:
140            return READ_WRITE_SYNC_ALL;
141        }
142        throw new IllegalArgumentException(mode);
143    }
144
145    private final int level;
146
147    private final String mode;
148
149    RandomAccessFileMode(final String mode, final int level) {
150        this.mode = mode;
151        this.level = level;
152    }
153
154    /**
155     * Performs an operation on the {@link RandomAccessFile} specified at the given {@link Path}.
156     * <p>
157     * This method allocates and releases the {@link RandomAccessFile} given to the consumer.
158     * </p>
159     *
160     * @param file the file specifying the {@link RandomAccessFile} to open.
161     * @param consumer the function to apply.
162     * @throws FileNotFoundException See {@link IORandomAccessFile#IORandomAccessFile(File, String)}.
163     * @throws IOException Thrown by the given function.
164     * @since 2.18.0
165     */
166    public void accept(final Path file, final IOConsumer<RandomAccessFile> consumer) throws IOException {
167        try (RandomAccessFile raf = create(file)) {
168            consumer.accept(raf);
169        }
170    }
171
172    /**
173     * Applies the given function for a {@link RandomAccessFile} specified at the given {@link Path}.
174     * <p>
175     * This method allocates and releases the {@link RandomAccessFile} given to the function.
176     * </p>
177     *
178     * @param <T> the return type of the function.
179     * @param file the file specifying the {@link RandomAccessFile} to open.
180     * @param function the function to apply.
181     * @return the function's result value.
182     * @throws FileNotFoundException See {@link IORandomAccessFile#IORandomAccessFile(File, String)}.
183     * @throws IOException Thrown by the given function.
184     * @since 2.18.0
185     */
186    public <T> T apply(final Path file, final IOFunction<RandomAccessFile, T> function) throws IOException {
187        try (RandomAccessFile raf = create(file)) {
188            return function.apply(raf);
189        }
190    }
191
192    /**
193     * Constructs a random access file to read from, and optionally to write to, the file specified by the {@link File} argument.
194     * <p>
195     * Prefer {@link #create(Path)} over this.
196     * </p>
197     *
198     * @param file the file object
199     * @return a random access file
200     * @throws FileNotFoundException See {@link IORandomAccessFile#IORandomAccessFile(File, String)}.
201     */
202    public RandomAccessFile create(final File file) throws FileNotFoundException {
203        return new IORandomAccessFile(file, mode);
204    }
205
206    /**
207     * Constructs a random access file to read from, and optionally to write to, the file specified by the {@link File} argument.
208     *
209     * @param file the file object
210     * @return a random access file
211     * @throws FileNotFoundException See {@link IORandomAccessFile#IORandomAccessFile(File, String)}.
212     */
213    public RandomAccessFile create(final Path file) throws FileNotFoundException {
214        return create(Objects.requireNonNull(file.toFile(), "file"));
215    }
216
217    /**
218     * Constructs a random access file to read from, and optionally to write to, the file specified by the {@link File} argument.
219     * <p>
220     * Prefer {@link #create(Path)} over this.
221     * </p>
222     *
223     * @param name the file object
224     * @return a random access file
225     * @throws FileNotFoundException See {@link IORandomAccessFile#IORandomAccessFile(File, String)}.
226     */
227    public RandomAccessFile create(final String name) throws FileNotFoundException {
228        return new IORandomAccessFile(name, mode);
229    }
230
231    /**
232     * A level for relative comparison of access mode rights, the larger, the more access.
233     * <p>
234     * The relative order from lowest to highest access rights is:
235     * </p>
236     * <ol>
237     * <li>{@link #READ_ONLY}</li>
238     * <li>{@link #READ_WRITE}</li>
239     * <li>{@link #READ_WRITE_SYNC_CONTENT}</li>
240     * <li>{@link #READ_WRITE_SYNC_ALL}</li>
241     * </ol>
242     * <p>
243     * This is unrelated to {@link #ordinal()}.
244     * </p>
245     *
246     * @return A level for relative comparison.
247     */
248    private int getLevel() {
249        return level;
250    }
251
252    /**
253     * Gets the access mode, one of {@value #R}, {@value #RW}, {@value #RWD}, or {@value #RWS}.
254     *
255     * @return one of {@value #R}, {@value #RW}, {@value #RWD}, or {@value #RWS}.
256     * @since 2.18.0
257     */
258    public String getMode() {
259        return mode;
260    }
261
262    /**
263     * Tests whether this mode implies the given {@code other} mode.
264     * <p>
265     * For example:
266     * </p>
267     * <ol>
268     * <li>{@link RandomAccessFileMode#READ_WRITE_SYNC_ALL} implies {{@link RandomAccessFileMode#READ_WRITE_SYNC_CONTENT}}.</li>
269     * <li>{@link RandomAccessFileMode#READ_WRITE_SYNC_CONTENT} implies {{@link RandomAccessFileMode#READ_WRITE}}.</li>
270     * <li>{@link RandomAccessFileMode#READ_WRITE} implies {{@link RandomAccessFileMode#READ_ONLY}}.</li>
271     * </ol>
272     *
273     * @param other the non-null mode to test against.
274     * @return whether this mode implies the given {@code other} mode.
275     * @since 2.18.0
276     */
277    public boolean implies(final RandomAccessFileMode other) {
278        // Note: The method name "implies" is inspired by java.security.Permission.implies(Permission)
279        return getLevel() >= other.getLevel();
280    }
281
282    /**
283     * Constructs a random access file to read from, and optionally to write to, the file specified by the {@link File} argument.
284     *
285     * @param name the file object
286     * @return a random access file
287     * @throws FileNotFoundException See {@link IORandomAccessFile#IORandomAccessFile(File, String)}.
288     * @since 2.18.0
289     */
290    public IORandomAccessFile io(final String name) throws FileNotFoundException {
291        return new IORandomAccessFile(name, mode);
292    }
293
294}