    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}