/* Copyright 2009 Hauke Rehfeld This file is part of QuakeInjector. QuakeInjector is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QuakeInjector is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QuakeInjector. If not, see <http://www.gnu.org/licenses/>. */ package de.haukerehfeld.quakeinjector; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.Properties; import java.util.Map; import java.util.HashMap; import java.lang.reflect.Field; /** * Handle the config/properties file and allow access to properties */ public class Configuration { private static final String CONFIGHEADER = "Quake Injector " + BuildCommit.buildCommit + " config file"; public class EnginePath extends FileValue { private EnginePath() { super("enginePath", null); } public File getUnzipDir(Package map) { String relativedir = map.getRelativeBaseDir(); String unzipdir = get().getAbsolutePath(); if (relativedir != null) { unzipdir += File.separator + relativedir; } return new File(unzipdir); } } public final EnginePath EnginePath = new EnginePath(); public class EngineExecutable extends FileValue { private EngineExecutable() { super("engineExecutable", new File("")); } } public final EngineExecutable EngineExecutable = new EngineExecutable(); public class WorkingDirAtExecutable extends BooleanValue { private WorkingDirAtExecutable() { super("workingDirAtExecutable", false); } } public final WorkingDirAtExecutable WorkingDirAtExecutable = new WorkingDirAtExecutable(); public class LocalDatabaseFile extends FileValue { private LocalDatabaseFile() { super("localDatabaseFile", new File("database.xml")); } } public final LocalDatabaseFile LocalDatabaseFile = new LocalDatabaseFile(); public class DownloadPath extends FileValue { private DownloadPath() { super("downloadPath", null); } public File get() { File result = super.get(); if (result == null) { System.out.println("downloadpath null, returning default"); return defaultPath(); } return result; } private File defaultPath() { if (EnginePath.get() == null) { return null; } return new File(EnginePath.get() + File.separator + "downloads"); } public void set(File v) { System.out.println("Setting downloadpath: " + v); if (v.equals(defaultPath()) || v.equals(new File(""))) { System.out.println(getClass() + " Setting downloadpath to null"); super.set(null); return; } super.set(v); } } public final DownloadPath DownloadPath = new DownloadPath(); public class EngineCommandLine extends StringValue { private EngineCommandLine() { super("engineCommandline", ""); } } public final EngineCommandLine EngineCommandLine = new EngineCommandLine(); public class RepositoryDatabasePath extends StringValue { private RepositoryDatabasePath() { super("repositoryDatabase", "https://www.quaddicted.com/reviews/quaddicted_database.xml"); } } public final RepositoryDatabasePath RepositoryDatabasePath = new RepositoryDatabasePath(); public class ScreenshotRepositoryPath extends StringValue { private ScreenshotRepositoryPath() { super("screenshotRepositoryPath", "https://www.quaddicted.com/reviews/screenshots/"); } } public final ScreenshotRepositoryPath ScreenshotRepositoryPath = new ScreenshotRepositoryPath(); public class ZipContentsDatabaseUrl extends StringValue { private ZipContentsDatabaseUrl() { super("ZipContentsDatabaseUrl", "http://haukerehfeld.de/projects/quakeinjector/temp/zipContents.xml"); } } public final ZipContentsDatabaseUrl ZipContentsDatabaseUrl = new ZipContentsDatabaseUrl(); public class RogueInstalled extends BooleanValue { private RogueInstalled() { super("rogueInstalled", false); } } public final RogueInstalled RogueInstalled = new RogueInstalled(); public class OfflineMode extends BooleanValue { private ChangeListenerList listeners = new ChangeListenerList(); private OfflineMode() { super("offlineMode", false); } public void addChangeListener(javax.swing.event.ChangeListener l) { listeners.addChangeListener(l); } @Override public void set(Boolean b) { super.set(b); listeners.notifyChangeListeners(this); } } public final OfflineMode OfflineMode = new OfflineMode(); public class HipnoticInstalled extends BooleanValue { private HipnoticInstalled() { super("hipnoticInstalled", false); } } public final HipnoticInstalled HipnoticInstalled = new HipnoticInstalled(); public class MainWindowPositionX extends IntegerValue { private MainWindowPositionX() { super("mainWindowPositionX", null); } } public final MainWindowPositionX MainWindowPositionX = new MainWindowPositionX(); public class MainWindowPositionY extends IntegerValue { private MainWindowPositionY() { super("mainWindowPositionY", null); } } public final MainWindowPositionY MainWindowPositionY = new MainWindowPositionY(); public class MainWindowWidth extends IntegerValue { private MainWindowWidth() { super("mainWindowWidth", null); } } public final MainWindowWidth MainWindowWidth = new MainWindowWidth(); public class MainWindowHeight extends IntegerValue { private MainWindowHeight() { super("mainWindowHeight", null); } } public final MainWindowHeight MainWindowHeight = new MainWindowHeight(); public class RepositoryBasePath extends StringValue { private final static String onlineRepositoryExtension = ".zip"; private RepositoryBasePath() { super("repositoryBase", "https://www.quaddicted.com/filebase/"); } /** * Get a complete Url to a map archive file in the repo */ public String getRepositoryUrl(String mapid) { return get() + mapid + onlineRepositoryExtension; } } public final RepositoryBasePath RepositoryBasePath = new RepositoryBasePath(); public final Map<String,Value<?>> All = new HashMap<String,Value<?>>(); private File configFile; public Configuration(File configFile) { this.configFile = configFile; //assign all fields to all list; Field[] fields = getClass().getDeclaredFields(); for (Field f: fields) { //System.out.println(f.getType()); //only value fields if (Value.class.isAssignableFrom(f.getType())) { try { All.put(f.getName(), (Value) f.get(this)); } catch (java.lang.IllegalAccessException e) { e.printStackTrace(); } } } init(); } @SuppressWarnings("unchecked") private void set(Configuration c) { for (Map.Entry<String,Value<?>> e: c.All.entrySet()) { String key = e.getKey(); Value<?> v = e.getValue(); if (!v.equals(getValue(key))) { getValue(key).set(v.get()); } } } private Value getValue(String key) { return All.get(key); } /** * Make sure config is loaded */ public void init() { read(); } /** * Read the properties file or get default properties */ private void read() { System.out.println("Reading configuration..."); Properties properties = new Properties(defaults()); //config exists if (configFile.canRead()) { try { FileInputStream in = new FileInputStream(configFile); properties.load(in); in.close(); } catch (java.io.FileNotFoundException e) { //this should never happen cause we just checked if we //can read -- but maybe another process fucks up... System.out.println("Can't read config file (" + e.getMessage() + ") even though i just checked if i can " + "read it. Using defaults..."); } catch (java.io.IOException e) { // if we can't read the config file, just use the defaults System.out.println("Couldn't read config file: " + e.getMessage() + ". Using defaults..."); } } set(properties); } @SuppressWarnings("unchecked") public void set(Properties p) { for (String key: All.keySet()) { String s = p.getProperty(key); if (s == null) { continue; } Value v = All.get(key); v.set(v.stringToValue(s)); //System.out.println("Setting " + key + ": " + p.getProperty(key)); } } public void get(Properties p) { for (String key: All.keySet()) { Value v = All.get(key); if (v.exists()) { //System.out.println("Writing " + key + " to " + v.toString() + " from " + v.getClass() + ": " + v); p.setProperty(key, v.toString()); } else { if (p.getProperty(key) != null) { p.remove(key); } } } } public void write() throws java.io.IOException { if (!Utils.canWriteToDirectory(configFile.getParentFile())) { System.out.println("Cannot write to config directory!"); throw new java.io.FileNotFoundException("Cannot write to config directory"); } System.out.print("Writing configuration..."); try { Properties properties = new Properties(defaults()); get(properties); FileOutputStream out = new FileOutputStream(configFile); properties.store(out, CONFIGHEADER); out.close(); } catch (java.io.FileNotFoundException e) { System.err.println("Can't write config file: " + e.getMessage()); e.printStackTrace(); } catch (java.io.IOException e) { System.err.println("Can't write config file: " + e.getMessage()); e.printStackTrace(); } System.out.println("done."); } /** * Get Default properties */ private Properties defaults() { Properties defaults = new Properties(); //System.out.println("Setting defaults: "); for (String key: All.keySet()) { Value<?> v = All.get(key); Object value = v.defaultValue(); //System.out.println("Setting defaults: " + v.key() + ", " + v.defaultValue()); if (value == null) { continue; } defaults.setProperty(v.key(), v.toString()); } return defaults; } public interface Value<T> { public T get(); public void set(T v); public String key(); public T defaultValue(); public T stringToValue(String s); /** * @return true if this has a value different from the default one */ public boolean exists(); /** * @return true if exists() or has a defaultvalue */ public boolean existsOrDefault(); } public abstract class AbstractValue<T> implements Value<T> { private String key; private T defaultValue; private T value; protected AbstractValue(String key, T defaultValue) { this.key = key; this.defaultValue = defaultValue; } public abstract T stringToValue(String v); public String key() { return key; } public T defaultValue() { return defaultValue; } public boolean existsOrDefault() { return exists() || defaultValue != null; } public T get() { if (value == null) { return defaultValue; } return value; } public void set(T v) { if (v == null || v.equals(defaultValue())) { //System.out.println(getClass() + ": Setting to null or default"); value = null; return; } value = v; } public boolean exists() { return value != null; } public String toString() { return existsOrDefault() ? get().toString() : null; } } public abstract class StringValue extends AbstractValue<String> { protected StringValue(String key, String defaultValue) { super(key, defaultValue); } public String stringToValue(String v) { return v; } public String toString() { return get(); } } public abstract class FileValue extends AbstractValue<File> { protected FileValue(String key, File defaultValue) { super(key, defaultValue); } public File stringToValue(String v) { return new File(v); } } public abstract class BooleanValue extends AbstractValue<Boolean> { protected BooleanValue(String key, boolean defaultValue) { super(key, defaultValue); } public Boolean stringToValue(String v) { return Boolean.valueOf(v); } } public abstract class IntegerValue extends AbstractValue<Integer> { protected IntegerValue(String key, Integer defaultValue) { super(key, defaultValue); } public Integer stringToValue(String v) { if (v == null) { return null; } try { return Integer.valueOf(v); } catch (java.lang.NumberFormatException e) { System.err.println(e); e.printStackTrace(); return null; } } } }