/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library 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 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.configure;
import java.io.File;
import java.util.LinkedHashMap;
import net.n3.nanoxml.XMLElement;
import org.jnode.configure.adapter.FileAdapter;
import org.jnode.configure.adapter.FileAdapterFactory;
/**
* A property set denotes a group of properties, typically associated with a
* file.
*
* @author crawley@jnode.org
*/
public class PropertySet {
public class Property {
private final String name;
private final PropertyType type;
private final String description;
private Value defaultValue;
private Value value;
private final XMLElement definingElement;
private final File definingFile;
public Property(String name, PropertyType type, String description, Value defaultValue,
XMLElement definingElement, File definingFile) {
super();
this.name = name;
this.type = type;
this.description = description;
this.defaultValue = defaultValue;
this.definingElement = definingElement;
this.definingFile = definingFile;
}
public Value getValue() {
return value == null ? defaultValue : value;
}
public void setValue(Value value) {
this.value = value;
}
public boolean isSet() {
return value != null;
}
public String getName() {
return name;
}
public PropertyType getType() {
return type;
}
public String getDescription() {
return description;
}
public XMLElement getDefiningElement() {
return definingElement;
}
public File getDefiningFile() {
return definingFile;
}
public boolean hasDefaultValue() {
return defaultValue != null;
}
public Value getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(Value defaultValue) {
this.defaultValue = defaultValue;
}
public boolean isControlProperty() {
return getPropertySet().getFile() == null;
}
public PropertySet getPropertySet() {
return PropertySet.this;
}
}
public static class Value {
private final String token;
private final String text;
public Value(String token, String text) {
super();
if (token.equals("")) {
throw new IllegalArgumentException("Empty 'token' string");
}
this.token = token;
this.text = text;
}
public String getToken() {
return token;
}
public String getText() {
return text;
}
public String toString() {
return "'" + token + "'/'" + text + "'";
}
}
private final File file;
private final File defaultFile;
private final File templateFile;
private final char marker;
private final ConfigureScript script;
private final FileAdapter adapter;
private final LinkedHashMap<String, Property> properties =
new LinkedHashMap<String, Property>();
public PropertySet(ConfigureScript script, File file, File defaultFile, File templateFile,
String fileFormat, char marker) throws ConfigureException {
this.file = file;
this.defaultFile = defaultFile;
this.templateFile = templateFile;
this.marker = marker;
this.script = script;
this.adapter = fileFormat == null ? null : FileAdapterFactory.createAdapter(fileFormat);
if (adapter != null) {
if (!adapter.isLoadSupported() && defaultFile != null) {
throw new ConfigureException("A '" + ScriptParser.DEFAULT_FILE +
"' attribute cannot be used with " + " format '" + fileFormat +
"': the format does not support property loading.");
}
if (!adapter.isSaveSupported() && templateFile == null) {
throw new ConfigureException("A '" + ScriptParser.TEMPLATE_FILE +
"' attribute is required with " + " format '" + fileFormat +
"': the format does not support property saving.");
}
}
}
public PropertySet(ConfigureScript script) throws ConfigureException {
this.file = null;
this.defaultFile = null;
this.templateFile = null;
this.marker = 0;
this.script = script;
this.adapter = null;
}
public void load(Configure configure) throws ConfigureException {
adapter.load(this, configure);
}
/**
* Save the properties in this property set to the property file. This
* method takes care of creating a backup file
*
* @param configure
* @throws ConfigureException
*/
public void save(Configure configure) throws ConfigureException {
if (file.exists()) {
File file = this.file.getAbsoluteFile();
File backup;
if (adapter.wasSourceGenerated(this)) {
backup = new File(file.getParentFile(), file.getName() + ".bak");
if (backup.exists()) {
if (!backup.delete()) {
throw new ConfigureException(
"Cannot delete existing '" + backup + "'");
}
}
} else {
backup = new File(file.getParentFile(), file.getName() + ".orig");
int no = 1;
while (backup.exists()) {
backup = new File(file.getParentFile(), file.getName() + ".orig" + ++no);
}
configure.output("Saving the current (non-generated!) " + file.getName() + " file");
configure.output("as " + backup);
}
if (!file.renameTo(backup)) {
throw new ConfigureException(
"Cannot rename existing '" + file + "' as '" + backup + "'");
}
configure.verbose("Renamed existing '" + file + "' as '" + backup + "'");
}
adapter.save(this, configure);
configure.verbose("Saved properties to '" + file + "'");
}
public File getFile() {
return file;
}
public File getDefaultFile() {
return defaultFile;
}
public File getTemplateFile() {
return templateFile;
}
public char getMarker() {
return marker;
}
public LinkedHashMap<String, Property> getProperties() {
return properties;
}
public void addProperty(String name, PropertyType propType, String description,
Value defaultValue, XMLElement definingElement, File definingFile)
throws ConfigureException {
Property oldProp = script.getProperty(name);
if (oldProp != null) {
// FIXME ... alternatively, we could allow properties to be defined
// in multiple
// contexts and have them refer to the same value.
throw new ConfigureException("Property '" + name + "' already declared at line " +
oldProp.getDefiningElement().getLineNr() + " of " + oldProp.getDefiningFile());
}
Property prop =
new Property(name, propType, description, defaultValue, definingElement,
definingFile);
properties.put(name, prop);
script.addProperty(prop);
}
public void setProperty(String name, Value value) throws ConfigureException {
Property property = properties.get(name);
if (property == null) {
throw new ConfigureException("Property not declared: '" + name + "'");
}
property.setValue(value);
}
public Property getProperty(String name) {
return properties.get(name);
}
}