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 *      https://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 */
017package org.apache.commons.io.input;
018
019import static org.apache.commons.io.IOUtils.EOF;
020
021import java.io.IOException;
022import java.io.Reader;
023
024import org.apache.commons.io.IOUtils;
025
026/**
027 * Always returns {@link IOUtils#EOF} to all attempts to read something from it.
028 * <p>
029 * Typically uses of this class include testing for corner cases in methods that accept readers and acting as a sentinel
030 * value instead of a {@code null} reader.
031 * </p>
032 *
033 * @since 2.7
034 */
035public class ClosedReader extends Reader {
036
037    /**
038     * The singleton instance.
039     *
040     * @since 2.12.0
041     */
042    public static final ClosedReader INSTANCE = new ClosedReader();
043
044    /**
045     * The singleton instance.
046     *
047     * @deprecated {@link #INSTANCE}.
048     */
049    @Deprecated
050    public static final ClosedReader CLOSED_READER = INSTANCE;
051
052    /**
053     * Construct a new instance.
054     */
055    public ClosedReader() {
056        // empty
057    }
058
059    @Override
060    public void close() throws IOException {
061        // noop
062    }
063
064    /**
065     * A no-op read method that always indicates end-of-stream.
066     *
067     * <p>Behavior:</p>
068     * <ul>
069     *   <li>If {@code len == 0}, returns {@code 0} immediately (no characters are read).</li>
070     *   <li>Otherwise, always returns {@value IOUtils#EOF} to signal that the stream is closed or at end-of-stream.</li>
071     * </ul>
072     *
073     * @param cbuf The destination buffer.
074     * @param off  The offset at which to start storing characters.
075     * @param len  The maximum number of characters to read.
076     * @return {@code 0} if {@code len == 0}; otherwise always {@value IOUtils#EOF}.
077     * @throws IndexOutOfBoundsException If {@code off < 0}, {@code len < 0}, or {@code off + len > cbuf.length}.
078     */
079    @Override
080    public int read(final char[] cbuf, final int off, final int len) {
081        IOUtils.checkFromIndexSize(cbuf, off, len);
082        if (len == 0) {
083            return 0;
084        }
085        return EOF;
086    }
087
088}