001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.core;
020
021import java.io.BufferedInputStream;
022import java.io.FileInputStream;
023import java.io.IOException;
024import java.io.Serializable;
025import java.util.*;
026import java.util.stream.IntStream;
027
028/**
029 * A {@link Configuration} implementation backed by a {@link java.util.Properties} object.
030 * @author apr@cs.com.uy
031 * @version $Id$
032 * @since jPOS 1.1
033 */
034public class SimpleConfiguration implements Configuration, Serializable {
035    /** Backing property store used by this configuration instance. */
036    private Properties props;
037
038    /** Default constructor. */
039    public SimpleConfiguration () {
040        props = new Properties();
041    }
042    /**
043     * Wraps the supplied {@link Properties} as a configuration.
044     *
045     * @param props backing properties (used by reference)
046     */
047    public SimpleConfiguration (Properties props) {
048        this.props = props;
049    }
050    /**
051     * Loads a configuration from a Java {@code .properties} file.
052     *
053     * @param filename path to the properties file
054     * @throws IOException if the file cannot be read
055     */
056    public SimpleConfiguration (String filename)
057        throws IOException
058    {
059        props = new Properties();
060        load (filename);
061    }
062
063    /**
064     * Returns the value of the configuration property named {@code name}, or the default value {@code def}.
065     *
066     * If the property value has the format <code>${xxx}</code> then its value is taken from a system property
067     * if it exists, or an environment variable. System property takes priority over environment variable.
068     *
069     * If the format is <code>$sys{...}</code> we read only a system property.
070     * if the format is <code>$env{...}</code> only an environment variable is used.
071     *
072     * @param name The configuration property key name.
073     * @param def  The default value.
074     * @return  The value stored under {@code name},
075     *          or {@code def} if there's no configuration property under the given {@code name}.
076     */
077    public String get (String name, String def) {
078        Object obj = props.get (name);
079        if (obj instanceof String[]) {
080            String[] arr= (String[]) obj;
081            obj = arr.length > 0 ? arr[0] : null;
082        } else if (obj instanceof List) {
083            List l = (List) obj;
084            obj = l.size() > 0 ? l.get(0) : null;
085        }
086        return (obj instanceof String) ? Environment.get((String) obj, def) : def;
087    }
088    public String[] getAll (String name) {
089        String[] ret;
090        Object obj = props.get (name);
091        if (obj instanceof String[]) {
092            ret = (String[]) obj;
093        } else if (obj instanceof String) {
094            ret = new String[1];
095            ret[0] = (String) obj;
096        } else
097            ret = new String[0];
098
099        Environment env = Environment.getEnvironment();
100        IntStream.range(0, ret.length).forEachOrdered(i -> ret[i] = env.getProperty(ret[i]));
101        return Arrays.stream(ret).filter(Objects::nonNull).toArray(String[]::new);
102    }
103    public int[] getInts (String name) {
104        String[] ss = getAll (name);
105        int[] ii = new int[ss.length];
106        for (int i=0; i<ss.length; i++)
107            ii[i] = Integer.parseInt(ss[i].trim());
108        return ii;
109    }
110    public long[] getLongs (String name) {
111        String[] ss = getAll (name);
112        long[] ll = new long[ss.length];
113        for (int i=0; i<ss.length; i++)
114            ll[i] = Long.parseLong(ss[i].trim());
115        return ll;
116    }
117    public double[] getDoubles (String name) {
118        String[] ss = getAll (name);
119        double[] dd = new double[ss.length];
120        for (int i=0; i<ss.length; i++)
121            dd[i] = Double.valueOf(ss[i].trim());
122        return dd;
123    }
124    public boolean[] getBooleans (String name) {
125        String[] ss = getAll (name);
126        boolean[] bb = new boolean[ss.length];
127        for (int i=0; i<ss.length; i++)
128            bb[i] = ss[i].equalsIgnoreCase("true") || ss[i].equalsIgnoreCase("yes");
129        return bb;
130    }
131    public String get (String name) {
132        return get(name, "");
133    }
134    public int getInt (String name) {
135        return Integer.parseInt(get(name, "0").trim());
136    }
137    public int getInt (String name, int def) {
138        return Integer.parseInt(get(name, Integer.toString(def)).trim());
139    }
140    public long getLong (String name) {
141        return Long.parseLong(get(name, "0").trim());
142    }
143    public long getLong (String name, long def) {
144        return Long.parseLong(get(name, Long.toString(def)).trim());
145    }
146    public double getDouble(String name) {
147        return Double.valueOf(get(name, "0.00").trim());
148    }
149    public double getDouble(String name, double def) {
150        return Double.valueOf(get(name, Double.toString(def)).trim());
151    }
152    public boolean getBoolean (String name) {
153        String v = get (name, "false").trim();
154        return v.equalsIgnoreCase("true") || v.equalsIgnoreCase("yes");
155    }
156    public boolean getBoolean (String name, boolean def) {
157        String v = get (name);
158        return v.length() == 0 ? def :
159                v.equalsIgnoreCase("true") || v.equalsIgnoreCase("yes");
160    }
161    /**
162     * Loads a Java {@code .properties} file into this configuration.
163     *
164     * @param filename path to the properties file
165     * @throws IOException if the file cannot be read
166     */
167    public void load(String filename)
168        throws IOException
169    {
170        FileInputStream fis = new FileInputStream(filename);
171        props.load(new BufferedInputStream(fis));
172        fis.close();
173    }
174    public synchronized void put (String name, Object value) {
175        props.put (name, value);
176    }
177    @Override
178    public Set<String> keySet() {
179        return props.stringPropertyNames();
180    }
181
182    @Override
183    public boolean equals(Object o) {
184        if (this == o) return true;
185        if (o == null || getClass() != o.getClass()) return false;
186        SimpleConfiguration that = (SimpleConfiguration) o;
187        return Objects.equals(props, that.props);
188    }
189
190    @Override
191    public int hashCode() {
192        return Objects.hash(props);
193    }
194
195    @Override
196    public String toString() {
197        return "SimpleConfiguration{" +
198          "props=" + props +
199          '}';
200    }
201
202    private static final long serialVersionUID = -6361797037366246968L;
203}