/*
* This file is part of Spoutcraft.
*
* Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org/>
* Spoutcraft is licensed under the GNU Lesser General Public License.
*
* Spoutcraft is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Spoutcraft 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.spoutcraft.client.config;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
/**
* <b>Settings Handler</b><br>
* A new better way of handling settings/configs without the complexity of
* YAML or using Bukkit's Configuration Class (If this is for a Bukkit plugin).
* @version 1.4
*/
public class SettingsHandler {
private File out;
private Boolean cached = false;
private String resource = null;
private HashMap<String,String> cache;
private InputStream input = null;
/**
* Constructor for the Settings/Config file.
* Note: The resource must be in the same package or sub package
* as this. If in a Sub directory the resource parameter
* is the path to it from this class file.
* @param String resource
* @param File out
*/
public SettingsHandler(String resource, String out) {
this.resource = resource;
this.out = new File(out);
}
/**
* Constructor for the Settings/Config file.
* Note: The resource must be in the same package or sub package
* as this. If in a sub directory the resource parameter
* is the path to it from this class file.
* @param String resource
* @param File out
*/
public SettingsHandler(String resource, File out) {
this.resource = resource;
this.out = out;
}
/**
* Constructor for the Settings/Config file.
* Note: I discourage the use of this constructor, as it is not a fully tested as it should be.
* @param InputStream input
* @param File out
*/
public SettingsHandler(InputStream input, File out) {
this.input = input;
this.out = out;
}
/**
* Constructor for the Settings/Config file.
* Note: This file must exist.
* @param String out
* @throws FileNotFoundException
*/
public SettingsHandler(File out) throws FileNotFoundException {
if (!out.exists()) {
throw new FileNotFoundException("The out does not exist.");
}
this.out = out;
}
/**
* Returns whether caching is enabled. <br>
* See {@link #setCached(Boolean) setCached(Boolean)} for more info on caching
* @return Boolean cached
*/
public Boolean isCached() {
return this.cached;
}
/**
*
* @param Boolean cached
*/
public void setCached(Boolean cached) {
this.cached = cached;
if (this.cached = false) {
this.cache = null;
}
}
/**
* Private method that takes the resource and writes it to the File out.
* @param String name
*/
private void create(String resource) {
InputStream input = getClass().getResourceAsStream(resource);
if (input != null) {
FileOutputStream output = null;
try {
//noinspection ResultOfMethodCallIgnored
out.getParentFile().mkdirs();
output = new FileOutputStream(out);
byte[] buf = new byte[8192];
int length;
while ((length = input.read(buf)) > 0) {
output.write(buf, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (Exception ignored) {
}
try {
if (output != null) {
output.close();
}
} catch (Exception ignored) {
}
}
}
}
/**
* Private method that takes the InputStream and it writes it to the File out
* @param InputStream input
*/
private void create(InputStream input) {
if (input != null) {
FileOutputStream output = null;
try {
output = new FileOutputStream(out);
byte[] buf = new byte[8192];
int length;
while ((length = input.read(buf)) > 0) {
output.write(buf, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (Exception ignored) {
}
try {
if (output != null) {
output.close();
}
} catch (Exception ignored) {
}
}
}
}
/**
* Private method that loads the properties in a HashMap.
* @return HashMap result
*/
private HashMap<String, String> loadHashMap() {
HashMap<String,String> result = new HashMap<String,String>();
try {
BufferedReader br = new BufferedReader(new FileReader(out));
String line;
while ((line = br.readLine()) != null) {
if ((line.isEmpty()) || (line.startsWith("#")) || (!line.contains(": "))) {
continue;
}
String[] args = line.split(": ");
if (args.length < 2) {
result.put(args[0], null);
continue;
}
result.put(args[0], args[1]);
}
br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return result;
}
/**
* Call this method before doing anything else with SettingsHandler.
* The only exception to this is {@link #setCached(Boolean) setCanced(Boolean)}.
* If caching is enabled then it will load the Properties into the cache.
*/
public void load() {
if (this.resource != null && !out.exists()) {
create(resource);
}
if (this.input != null && !out.exists()) {
create(input);
}
if (this.cached) {
this.cache = this.loadHashMap();
}
}
/**
* Returns the value of a property as a String
* @param String property
* @return String value
*/
public String getPropertyString(String property) {
try {
if (this.cached) {
return this.cache.get(property);
} else {
HashMap<String,String> contents = this.loadHashMap();
return contents.get(property);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Returns the value of a property as a Integer
* @param String property
* @return Integer value
*/
public Integer getPropertyInteger(String property) {
try {
if (this.cached) {
return Integer.parseInt(this.cache.get(property));
} else {
HashMap<String,String> contents = this.loadHashMap();
return Integer.parseInt(contents.get(property));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Returns the value of a property as a Boolean
* @param String property
* @return Boolean value
*/
public Boolean getPropertyBoolean(String property) {
try {
String result;
if (this.cached) {
result = this.cache.get(property);
} else {
HashMap<String,String> contents = this.loadHashMap();
result = contents.get(property);
}
if (result == null) {
return false;
}
if (result.equalsIgnoreCase("true") || result.equalsIgnoreCase("false")) {
return Boolean.valueOf(result.toLowerCase());
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Returns the value of a property as a Double
* @param String property
* @return Double value
*/
public Double getPropertyDouble(String property) {
try {
String result;
if (this.cached) {
result = this.cache.get(property);
} else {
HashMap<String,String> contents = this.loadHashMap();
result = contents.get(property);
}
if (!result.contains(".")) {
result += ".0";
}
return Double.parseDouble(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Returns true if a property exists
* @param String property
* @return Boolean check
*/
public Boolean checkProperty(String property) {
String check;
try {
if (this.cached) {
check = this.cache.get(property);
} else {
HashMap<String,String> contents = this.loadHashMap();
check = contents.get(property);
}
if (check != null) {
return true;
}
} catch (Exception e) {
return false;
}
return false;
}
// Editing the Settings/Config File Methods \\
/**
* Private method that writes out the new Settings/Config file after changes are made.
* If caching is enabled, it will call the
* {@link #load() load()} method.
*
* @param HashMap newContents
*/
private void flush(HashMap<Integer,String> newContents) {
try {
this.delFile(out);
//noinspection ResultOfMethodCallIgnored
out.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(out));
for (int i = 1; i <= newContents.size(); i ++) {
String line = newContents.get(i);
if (line == null) {
writer.append("\n");
continue;
}
writer.append(line);
writer.append("\n");
}
writer.flush();
writer.close();
if (cached) {
this.load();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Private method used by {@link #flush(HashMap) flush(HashMap)} to delete the old
* Settings/Config file.
* @param File file
*/
private void delFile(File file) {
if (file.exists()) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
/**
* Private method to get a HashMap of the contents of the Settings/Contents file.
* They are stored as lines indexed by the line number.
* @return HashMap contents
*/
private HashMap<Integer,String> getAllFileContents() {
HashMap<Integer,String> result = new HashMap<Integer,String>();
Integer i = 1;
try {
BufferedReader br = new BufferedReader(new FileReader(out));
String line;
while ((line = br.readLine()) != null) {
if (line.isEmpty()) {
result.put(i, null);
i ++;
continue;
}
result.put(i, line);
i ++;
}
br.close();
} catch (Exception ex) {
//ex.printStackTrace();
}
return result;
}
/**
* Allows you to add a comment to the end of the end of the file.
* @param String comment
*/
public void insertComment(String comment) {
HashMap<Integer,String> contents = this.getAllFileContents();
contents.put(contents.size() + 1, "#" + comment);
this.flush(contents);
}
/**
* Allows to add a comment at the line that you specify.
* @param String comment
* @param Integer line
*/
public void insertComment(String comment, Integer line) {
HashMap<Integer,String> contents = this.getAllFileContents();
if (line >= contents.size() + 1) {
return;
}
HashMap<Integer,String> newContents = new HashMap<Integer,String>();
for (int i = 1; i < line; i ++) {
newContents.put(i, contents.get(i));
}
newContents.put(line, "#" + comment);
for (int i = line; i <= contents.size(); i ++) {
newContents.put(i + 1, contents.get(i));
}
this.flush(newContents);
}
/**
* Allows you to add a property at the end of the file.
* @param String property
* @param Object obj
*/
public void put(String property, Object obj) {
HashMap<Integer,String> contents = this.getAllFileContents();
contents.put(contents.size() + 1, property + ": " + obj.toString());
this.flush(contents);
}
/**
* Allows you to add a property at the line that you specify
* @param String property
* @param Object obj
* @param Integer line
*/
public void put(String property, Object obj, Integer line) {
HashMap<Integer,String> contents = this.getAllFileContents();
if (line >= contents.size() + 1) {
return;
}
HashMap<Integer,String> newContents = new HashMap<Integer,String>();
for (int i = 1; i < line; i ++) {
newContents.put(i, contents.get(i));
}
newContents.put(line, property + ": " + obj.toString());
for (int i = line; i <= contents.size(); i ++) {
newContents.put(i + 1, contents.get(i));
}
this.flush(newContents);
}
/**
* Allows you to change the value of a property.
* @param String property
* @param Object obj
*/
public void changeProperty(String property, Object obj) {
HashMap<Integer,String> contents = this.getAllFileContents();
if ((contents == null)) {
return;
}
for (int i = 1; i <= contents.size(); i ++) {
if (contents.get(i) == null) {
continue;
}
String check = contents.get(i);
if (check.startsWith(property)) {
check = check.replace(property, "");
if (!(check.startsWith(": "))) {
continue;
}
contents.remove(i);
contents.put(i, property + ": " + obj.toString());
}
}
this.flush(contents);
}
/**
* Returns the amount of the lines in the file. It ignores the last line if it is empty.
* @return Integer lineCount
*/
public Integer getLineCount() {
HashMap<Integer,String> contents = this.getAllFileContents();
return contents.size();
}
}