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.collections4.properties;
019
020import java.io.File;
021import java.io.FileNotFoundException;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.Reader;
025import java.net.URI;
026import java.net.URL;
027import java.nio.file.Files;
028import java.nio.file.Path;
029import java.nio.file.Paths;
030import java.util.Objects;
031import java.util.Properties;
032
033/**
034 * Subclasses create and load {@link Properties} and subclasses of {@link Properties} like {@link SortedProperties}.
035 *
036 * @param <T> {@link Properties} or a subclass like {@link SortedProperties}.
037 * @see Properties
038 * @since 4.4
039 */
040public abstract class AbstractPropertiesFactory<T extends Properties> {
041
042    /**
043     * Enumerates property formats.
044     *
045     * @since 4.5.0-M1
046     */
047    public enum PropertyFormat {
048
049        /** Properties file format. */
050        PROPERTIES,
051
052        /** XML file format. */
053        XML;
054
055        static PropertyFormat toPropertyFormat(final String fileName) {
056            return Objects.requireNonNull(fileName, "fileName").endsWith(".xml") ? XML : PROPERTIES;
057        }
058    }
059
060    /**
061     * Constructs an instance.
062     */
063    protected AbstractPropertiesFactory() {
064        // no init.
065    }
066
067    /**
068     * Subclasses override to provide customized properties instances.
069     *
070     * @return a new Properties instance.
071     */
072    protected abstract T createProperties();
073
074    /**
075     * Creates and loads properties from the given file.
076     *
077     * @param classLoader the class loader to use to get the named resource.
078     * @param name        the location of the properties file.
079     * @return a new properties object.
080     * @throws IOException              Thrown if an error occurred reading the input stream.
081     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
082     */
083    public T load(final ClassLoader classLoader, final String name) throws IOException {
084        try (InputStream inputStream = classLoader.getResourceAsStream(name)) {
085            return load(inputStream, PropertyFormat.toPropertyFormat(name));
086        }
087    }
088
089    /**
090     * Creates and loads properties from the given file.
091     *
092     * @param file the location of the properties file.
093     * @return a new properties object.
094     * @throws IOException              Thrown if an error occurred reading the input stream.
095     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
096     * @throws FileNotFoundException    Thrown if the file does not exist, is a directory, or cannot be opened for
097     *                                  reading.
098     * @throws SecurityException        Thrown if a security manager's {@code checkRead} method denies read access to
099     *                                  the file.
100     */
101    public T load(final File file) throws FileNotFoundException, IOException {
102        return load(file.toPath());
103    }
104
105    /**
106     * Creates and loads properties from the given input stream.
107     *
108     * @param inputStream the location of the properties file.
109     * @return a new properties object.
110     * @throws IOException              Thrown if an error occurred reading the input stream.
111     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
112     */
113    public T load(final InputStream inputStream) throws IOException {
114        if (inputStream == null) {
115            return null;
116        }
117        final T properties = createProperties();
118        properties.load(inputStream);
119        return properties;
120    }
121
122    /**
123     * Creates and loads properties from the given input stream.
124     *
125     * @param inputStream the location of the properties file.
126     * @param propertyFormat The format of the given file.
127     * @return a new properties object.
128     * @throws IOException              Thrown if an error occurred reading the input stream.
129     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
130     * @since 4.5.0-M1
131     */
132    public T load(final InputStream inputStream, final PropertyFormat propertyFormat) throws IOException {
133        if (inputStream == null) {
134            return null;
135        }
136        final T properties = createProperties();
137        if (propertyFormat == PropertyFormat.XML) {
138            properties.loadFromXML(inputStream);
139        } else {
140            properties.load(inputStream);
141        }
142        return properties;
143    }
144
145    /**
146     * Creates and loads properties from the given path.
147     *
148     * @param path the location of the properties file.
149     * @return a new properties object.
150     * @throws IOException              Thrown if an error occurred reading the input stream.
151     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
152     */
153    public T load(final Path path) throws IOException {
154        try (InputStream inputStream = Files.newInputStream(path)) {
155            return load(inputStream, PropertyFormat.toPropertyFormat(Objects.toString(path.getFileName(), null)));
156        }
157    }
158
159    /**
160     * Creates and loads properties from the given reader.
161     *
162     * @param reader the location of the properties file.
163     * @return a new properties object.
164     * @throws IOException              Thrown if an error occurred reading the input stream.
165     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
166     */
167    public T load(final Reader reader) throws IOException {
168        final T properties = createProperties();
169        properties.load(reader);
170        return properties;
171    }
172
173    /**
174     * Creates and loads properties from the given file name.
175     *
176     * @param name the location of the properties file.
177     * @return a new properties object.
178     * @throws IOException              Thrown if an error occurred reading the input stream.
179     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
180     */
181    public T load(final String name) throws IOException {
182        return load(Paths.get(name));
183    }
184
185    /**
186     * Creates and loads properties from the given URI.
187     *
188     * @param uri the location of the properties file.
189     * @return a new properties object.
190     * @throws IOException              Thrown if an error occurred reading the input stream.
191     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
192     */
193    public T load(final URI uri) throws IOException {
194        return load(Paths.get(uri));
195    }
196
197    /**
198     * Creates and loads properties from the given URL.
199     *
200     * @param url the location of the properties file.
201     * @return a new properties object.
202     * @throws IOException              Thrown if an error occurred reading the input stream.
203     * @throws IllegalArgumentException Thrown if the input contains a malformed Unicode escape sequence.
204     */
205    public T load(final URL url) throws IOException {
206        try (InputStream inputStream = url.openStream()) {
207            return load(inputStream, PropertyFormat.toPropertyFormat(url.getFile()));
208        }
209    }
210
211}