# MemoryUsageSetting.java

    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.apache.pdfbox.io;

    import java.io.File;

    /**
     * Controls how memory/temporary files are used for
     * buffering streams etc.
     */
    public final class MemoryUsageSetting
    {
        private final boolean useMainMemory;
        private final boolean useTempFile;

        /** maximum number of main-memory bytes allowed to be used;
         *  <code>-1</code> means 'unrestricted' */
        private final long maxMainMemoryBytes;

        /** maximum number of bytes allowed for storage at all (main-memory+file);
         *  <code>-1</code> means 'unrestricted' */
        private final long maxStorageBytes;

        /** directory to be used for scratch file */
        private File tempDir;

        /**
         * Private constructor for setup buffering memory usage called by one of the setup methods.
         *
         * @param useMainMemory if <code>true</code> main memory usage is enabled; in case of
         *                      <code>false</code> and <code>useTempFile</code> is <code>false</code> too
         *                      we set this to <code>true</code>
         * @param useTempFile if <code>true</code> using of temporary file(s) is enabled
         * @param maxMainMemoryBytes maximum number of main-memory to be used;
         *                           if <code>-1</code> means 'unrestricted';
         *                           if <code>0</code> we only use temporary file if <code>useTempFile</code>
         *                           is <code>true</code> otherwise main-memory usage will have restriction
         *                           defined by maxStorageBytes
         * @param maxStorageBytes maximum size the main-memory and temporary file(s) may have all together;
         *                        <code>0</code>  or less will be ignored; if it is less than
         *                        maxMainMemoryBytes we use maxMainMemoryBytes value instead
         */
        private MemoryUsageSetting(boolean useMainMemory, boolean useTempFile,
                            long maxMainMemoryBytes, long maxStorageBytes)
        {
            // do some checks; adjust values as needed to get consistent setting
            boolean locUseMainMemory = !useTempFile || useMainMemory;
            long    locMaxMainMemoryBytes = useMainMemory ? maxMainMemoryBytes : -1;
            long    locMaxStorageBytes = maxStorageBytes > 0 ? maxStorageBytes : -1;

            if (locMaxMainMemoryBytes < -1)
            {
                locMaxMainMemoryBytes = -1;
            }

            if (locUseMainMemory && (locMaxMainMemoryBytes == 0))
            {
                if (useTempFile) {
                    locUseMainMemory = false;
                }
                else
                {
                    locMaxMainMemoryBytes = locMaxStorageBytes;
                }
            }

            if (locUseMainMemory && (locMaxStorageBytes > -1) &&
                ((locMaxMainMemoryBytes == -1) || (locMaxMainMemoryBytes > locMaxStorageBytes)))
            {
                locMaxStorageBytes = locMaxMainMemoryBytes;
            }

            this.useMainMemory = locUseMainMemory;
            this.useTempFile = useTempFile;
            this.maxMainMemoryBytes = locMaxMainMemoryBytes;
            this.maxStorageBytes = locMaxStorageBytes;
        }

        /**
         * Setups buffering memory usage to only use main-memory (no temporary file)
         * which is not restricted in size.
         */
        public static MemoryUsageSetting setupMainMemoryOnly()
        {
            return setupMainMemoryOnly(-1);
        }

        /**
         * Setups buffering memory usage to only use main-memory with the defined maximum.
         *
         * @param maxMainMemoryBytes maximum number of main-memory to be used;
         *                           <code>-1</code> for no restriction;
         *                           <code>0</code> will also be interpreted here as no restriction
         */
        public static MemoryUsageSetting setupMainMemoryOnly(long maxMainMemoryBytes)
        {
            return new MemoryUsageSetting(true, false, maxMainMemoryBytes, maxMainMemoryBytes);
        }

        /**
         * Setups buffering memory usage to only use temporary file(s) (no main-memory)
         * with not restricted size.
         */
        public static MemoryUsageSetting setupTempFileOnly()
        {
            return setupTempFileOnly(-1);
        }

        /**
         * Setups buffering memory usage to only use temporary file(s) (no main-memory)
         * with the specified maximum size.
         *
         * @param maxStorageBytes maximum size the temporary file(s) may have all together;
         *                        <code>-1</code> for no restriction;
         *                        <code>0</code> will also be interpreted here as no restriction
         */
        public static MemoryUsageSetting setupTempFileOnly(long maxStorageBytes)
        {
            return new MemoryUsageSetting(false, true, 0, maxStorageBytes);
        }

        /**
         * Setups buffering memory usage to use a portion of main-memory and additionally
         * temporary file(s) in case the specified portion is exceeded.
         *
         * @param maxMainMemoryBytes maximum number of main-memory to be used;
         *                           if <code>-1</code> this is the same as {@link #setupMainMemoryOnly()};
         *                           if <code>0</code> this is the same as {@link #setupTempFileOnly()}
         */
        public static MemoryUsageSetting setupMixed(long maxMainMemoryBytes)
        {
            return setupMixed(maxMainMemoryBytes, -1);
        }

        /**
         * Setups buffering memory usage to use a portion of main-memory and additionally
         * temporary file(s) in case the specified portion is exceeded.
         *
         * @param maxMainMemoryBytes maximum number of main-memory to be used;
         *                           if <code>-1</code> this is the same as {@link #setupMainMemoryOnly()};
         *                           if <code>0</code> this is the same as {@link #setupTempFileOnly()}
         * @param maxStorageBytes maximum size the main-memory and temporary file(s) may have all together;
         *                        <code>0</code>  or less will be ignored; if it is less than
         *                        maxMainMemoryBytes we use maxMainMemoryBytes value instead
         */
        public static MemoryUsageSetting setupMixed(long maxMainMemoryBytes, long maxStorageBytes)
        {
            return new MemoryUsageSetting(true, true, maxMainMemoryBytes, maxStorageBytes);
        }

        /**
         * Returns a copy of this instance with the maximum memory/storage restriction
         * divided by the provided number of parallel uses.
         *
         * @param parallelUseCount specifies the number of parallel usages for the setting to
         *                         be returned
         *
         * @return a copy from this instance with the maximum memory/storage restrictions
         *         adjusted to the multiple usage
         */
        public MemoryUsageSetting getPartitionedCopy(int parallelUseCount)
        {
            long newMaxMainMemoryBytes = maxMainMemoryBytes <= 0 ? maxMainMemoryBytes :
                                                                   maxMainMemoryBytes / parallelUseCount;
            long newMaxStorageBytes = maxStorageBytes <= 0 ? maxStorageBytes :
                                                             maxStorageBytes / parallelUseCount;

            MemoryUsageSetting copy = new MemoryUsageSetting( useMainMemory, useTempFile,
                                                              newMaxMainMemoryBytes, newMaxStorageBytes );
            copy.tempDir = tempDir;

            return copy;
        }

        /**
         * Sets directory to be used for temporary files.
         *
         * @param tempDir directory for temporary files
         *
         * @return this instance
         */
        public MemoryUsageSetting setTempDir(File tempDir)
        {
            this.tempDir = tempDir;
            return this;
        }

        /**
         * Returns <code>true</code> if main-memory is to be used.
         *
         * <p>If this returns <code>false</code> it is ensured {@link #useTempFile()}
         * returns <code>true</code>.</p>
         */
        public boolean useMainMemory()
        {
            return useMainMemory;
        }

        /**
         * Returns <code>true</code> if temporary file is to be used.
         *
         * <p>If this returns <code>false</code> it is ensured {@link #useMainMemory}
         * returns <code>true</code>.</p>
         */
        public boolean useTempFile()
        {
            return useTempFile;
        }

        /**
         * Returns <code>true</code> if maximum main memory is restricted to a specific
         * number of bytes.
         */
        public boolean isMainMemoryRestricted()
        {
            return maxMainMemoryBytes >= 0;
        }

        /**
         * Returns <code>true</code> if maximum amount of storage is restricted to a specific
         * number of bytes.
         */
        public boolean isStorageRestricted()
        {
            return maxStorageBytes > 0;
        }

        /**
         * Returns maximum size of main-memory in bytes to be used.
         */
        public long getMaxMainMemoryBytes()
        {
            return maxMainMemoryBytes;
        }

        /**
         * Returns maximum size of storage bytes to be used
         * (main-memory in temporary files all together).
         */
        public long getMaxStorageBytes()
        {
            return maxStorageBytes;
        }

        /**
         * Returns directory to be used for temporary files or <code>null</code>
         * if it was not set.
         */
        public File getTempDir()
        {
            return tempDir;
        }

        @Override
        public String toString()
        {
            return useMainMemory ?
                       (useTempFile ? "Mixed mode with max. of " + maxMainMemoryBytes + " main memory bytes" +
                                      (isStorageRestricted() ? " and max. of " + maxStorageBytes + " storage bytes" :
                                                               " and unrestricted scratch file size") :
                                      (isMainMemoryRestricted() ? "Main memory only with max. of " + maxMainMemoryBytes + " bytes" :
                                                                  "Main memory only with no size restriction")):
                       (isStorageRestricted() ? "Scratch file only with max. of " + maxStorageBytes + " bytes" :
                                                "Scratch file only with no size restriction");
        }
    }