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.q2;
020
021import java.io.File;
022import java.io.FileInputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Map;
028import java.util.Properties;
029
030import org.jdom2.Element;
031import org.jpos.core.Configuration;
032import org.jpos.core.ConfigurationException;
033import org.jpos.core.Environment;
034import org.jpos.core.SimpleConfiguration;
035import org.jpos.iso.ISOUtil;
036import org.yaml.snakeyaml.Yaml;
037
038public class SimpleConfigurationFactory implements ConfigurationFactory {
039    @Override
040    public Configuration getConfiguration(Element e) throws ConfigurationException {
041        Properties props = new Properties();
042        for (Element property : e.getChildren("property")) {
043            String name = property.getAttributeValue("name");
044            String value = property.getAttributeValue("value");
045            String baseFile = property.getAttributeValue("file");
046            if (baseFile != null) {
047                boolean isEnv = Boolean.parseBoolean(property.getAttributeValue("env", "false"));
048                processFile(props, baseFile, isEnv);
049            } else if (name != null && value != null) {
050                processProperty(props, name, value);
051            }
052        }
053        return new SimpleConfiguration(props);
054    }
055
056    protected void processProperty(Properties props, String name, String value) {
057        Object obj = props.get(name);
058        if (obj instanceof String[]) {
059            String[] mobj = (String[]) obj;
060            String[] m = new String[mobj.length + 1];
061            System.arraycopy(mobj, 0, m, 0, mobj.length);
062            m[mobj.length] = value;
063            props.put(name, m);
064        } else if (obj instanceof String) {
065            String[] m = new String[2];
066            m[0] = (String) obj;
067            m[1] = value;
068            props.put(name, m);
069        } else
070            props.put(name, value);
071    }
072
073    protected void processFile(Properties props, String baseFile, boolean isEnv) throws ConfigurationException {
074        baseFile = Environment.get(baseFile);
075        boolean foundFile = false;
076        for (String file : getFiles(baseFile, isEnv?Environment.getEnvironment().getName():"")) {
077            foundFile |= readYamlFile(props, file);
078        }
079        if (!foundFile) {
080            throw new ConfigurationException("Could not find any matches for file: " + baseFile);
081        }
082    }
083
084    protected List<String> getFiles(String baseFile, String environmnents) {
085        List<String> files = new ArrayList<>();
086        files.add(baseFile);
087        if (baseFile.endsWith(".yml") || baseFile.endsWith(".properties")) {
088            baseFile = baseFile.substring(0, baseFile.lastIndexOf("."));
089        }
090        for (String env : ISOUtil.commaDecode(environmnents)) {
091            if (!ISOUtil.isBlank(env)) {
092                files.add(baseFile + "-" + env + ".yml");
093                files.add(baseFile + "-" + env + ".properties");
094            }
095        }
096        return files;
097    }
098
099    protected boolean readYamlFile(Properties props, String fileName) throws ConfigurationException {
100        try {
101            if (fileName.endsWith(".yml")) {
102                return readYAML(props, fileName);
103            } else {
104                return readPropertyFile(props, fileName);
105            }
106        } catch (Exception ex) {
107            throw new ConfigurationException(fileName, ex);
108        }
109    }
110
111    protected boolean readPropertyFile(Properties props, String fileName) throws IOException {
112        File f = new File(fileName);
113        if (f.exists() && f.canRead()) {
114            try (FileInputStream in = new FileInputStream(f)) {
115                props.load(in);
116                return true;
117            }
118        }
119        return false;
120    }
121
122    protected boolean readYAML(Properties props, String fileName) throws IOException {
123        File f = new File(fileName);
124        if (f.exists() && f.canRead()) {
125            try (InputStream fis = new FileInputStream(f)) {
126                Yaml yaml = new Yaml();
127                Iterable<Object> document = yaml.loadAll(fis);
128                document.forEach(d -> {
129                    Environment.flat(props, null, (Map<String, Object>) d, true);
130                });
131            }
132            return true;
133        }
134        return false;
135    }
136}