/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.preferences;
import icy.util.ClassUtil;
import icy.util.StringUtil;
import icy.util.XMLUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* @author Stephane
*/
public class XMLPreferences
{
public static class XMLPreferencesRoot
{
private final String filename;
private Document doc;
// cached
Element element;
XMLPreferences preferences;
public XMLPreferencesRoot(String filename)
{
this.filename = filename;
load();
}
/**
* Load preferences from file
*/
public void load()
{
load(filename);
}
/**
* Load preferences from file
*/
public void load(String filename)
{
try
{
// get document
doc = XMLUtil.loadDocument(new File(filename));
}
catch (Throwable t)
{
System.err.println("Error: " + filename + " preferences file is corrupted, cannot recover settings.");
// corrupted XML file
doc = null;
}
// create it if not existing
if (doc == null)
doc = XMLUtil.createDocument(false);
// create root element
element = XMLUtil.createRootElement(doc);
// create our root XMLPreference object
preferences = new XMLPreferences(this, element);
preferences.clean();
}
/**
* Save preferences to file
*/
public void save()
{
save(filename);
}
/**
* Save preferences to file
*/
public void save(String filename)
{
if (doc != null)
XMLUtil.saveDocument(doc, new File(filename));
}
/**
* @return the element
*/
public Element getElement()
{
return element;
}
/**
* @return the preferences
*/
public XMLPreferences getPreferences()
{
return preferences;
}
}
private final static String TYPE_SECTION = "section";
private final static String TYPE_KEY = "key";
private final XMLPreferencesRoot root;
private final Element currentElement;
/**
* Use:<br>
* <code>new XMLPreferencesRoot(filename).getPreferences()</code><br>
* to load preferences from file.
*/
XMLPreferences(XMLPreferencesRoot root, Element element)
{
super();
this.root = root;
currentElement = element;
}
public String absolutePath()
{
String result = "/" + name();
synchronized (root)
{
Element parent = XMLUtil.getParentElement(currentElement);
while ((parent != null) && (parent != root.element))
{
result = "/" + XMLUtil.getGenericElementName(parent) + result;
parent = XMLUtil.getParentElement(parent);
}
}
return result;
}
public String name()
{
synchronized (root)
{
return XMLUtil.getGenericElementName(currentElement);
}
}
public XMLPreferences getParent()
{
final Element parent;
synchronized (root)
{
parent = XMLUtil.getParentElement(currentElement);
}
if (parent != null)
return new XMLPreferences(root, parent);
return null;
}
public ArrayList<XMLPreferences> getChildren()
{
final ArrayList<XMLPreferences> result = new ArrayList<XMLPreferences>();
final List<Element> elements;
synchronized (root)
{
elements = XMLUtil.getGenericElements(currentElement, TYPE_SECTION);
}
for (Element element : elements)
result.add(new XMLPreferences(root, element));
return result;
}
public ArrayList<String> childrenNames()
{
final ArrayList<String> result = new ArrayList<String>();
synchronized (root)
{
for (Element element : XMLUtil.getGenericElements(currentElement, TYPE_SECTION))
result.add(XMLUtil.getGenericElementName(element));
}
return result;
}
private Element getSection(String name)
{
if (StringUtil.isEmpty(name))
return currentElement;
Element element;
// absolute path
if (name.startsWith("/"))
element = root.element;
else
{
// we test first current node is still existing
if (!exists())
return null;
element = currentElement;
}
synchronized (root)
{
for (String subName : name.split("/"))
if (!subName.isEmpty())
element = XMLUtil.getGenericElement(element, TYPE_SECTION, subName);
}
return element;
}
private Element setSection(String name)
{
if (StringUtil.isEmpty(name))
return currentElement;
Element element;
// absolute path
if (name.startsWith("/"))
element = root.element;
else
{
// we test first current node is still existing
if (!exists())
return null;
element = currentElement;
}
synchronized (root)
{
for (String subName : name.split("/"))
if (!subName.isEmpty())
element = XMLUtil.setGenericElement(element, TYPE_SECTION, subName);
}
return element;
}
/**
* Return XMLPreferences of specified node.<br>
*/
public XMLPreferences node(String name)
{
final Element element = setSection(name);
if (element != null)
return new XMLPreferences(root, element);
return null;
}
/**
* Return XMLPreferences of specified node using class name of specified object.<br>
* <code>nodeForClass(object) == node(object.getClass().getName())</code><br>
* Ex : <code>nodeForClass("text") == node("java.lang.String")</code>
*/
public XMLPreferences nodeForClass(Object object)
{
if (object != null)
return node(ClassUtil.getPathFromQualifiedName(object.getClass().getName()));
return null;
}
/**
* Return the {@link XMLPreferences} node as an XML node.
*/
public Element getXMLNode()
{
return currentElement;
}
/**
* Return true if current node is existing
*/
public boolean exists()
{
// root element, always exists
if (currentElement == root.element)
return true;
synchronized (root)
{
// try to reach root from current element
Element parent = XMLUtil.getParentElement(currentElement);
while (parent != null)
{
// we reached root so the element still exist
if (parent == root.element)
return true;
parent = XMLUtil.getParentElement(parent);
}
}
// can't reach root, element is no more existing
return false;
}
/**
* Return true if specified node exists
*/
public boolean nodeExists(String name)
{
return getSection(name) != null;
}
/**
* Return true if node for specified object exists.<br>
* <code>nodeForClassExists(object) == nodeExists(object.getClass().getName())</code><br>
* Ex : <code>nodeForClassExists("text") == nodeExists("java.lang.String")</code>
*/
public boolean nodeForClassExists(Object object)
{
if (object != null)
return nodeExists(ClassUtil.getPathFromQualifiedName(object.getClass().getName()));
return false;
}
public ArrayList<String> keys()
{
final ArrayList<String> result = new ArrayList<String>();
synchronized (root)
{
for (Element element : XMLUtil.getGenericElements(currentElement, TYPE_KEY))
result.add(XMLUtil.getGenericElementName(element));
}
return result;
}
/**
* Remove all non element nodes
*/
public void clean()
{
synchronized (root)
{
final List<Node> nodes = XMLUtil.getChildren(currentElement);
for (Node node : nodes)
{
final String nodeName = node.getNodeName();
if (!(nodeName.equals(TYPE_KEY) || nodeName.equals(TYPE_SECTION)))
XMLUtil.removeNode(currentElement, node);
}
}
}
/**
* Remove all direct children of this node
*/
public void clear()
{
synchronized (root)
{
XMLUtil.removeChildren(currentElement, TYPE_KEY);
}
}
/**
* Remove specified element
*/
private void remove(Element element)
{
if (element != null)
{
synchronized (root)
{
final Element parent = XMLUtil.getParentElement(element);
if (parent != null)
XMLUtil.removeNode(parent, element);
}
}
}
/**
* Remove current section
*/
public void remove()
{
remove(currentElement);
}
/**
* Remove specified section
*/
public void remove(String name)
{
remove(getSection(name));
}
/**
* Remove all sections
*/
public void removeChildren()
{
synchronized (root)
{
XMLUtil.removeChildren(currentElement, TYPE_SECTION);
}
}
public String get(String key, String def)
{
synchronized (root)
{
return XMLUtil.getGenericElementValue(currentElement, TYPE_KEY, key, def);
}
}
public boolean getBoolean(String key, boolean def)
{
synchronized (root)
{
return XMLUtil.getGenericElementBooleanValue(currentElement, TYPE_KEY, key, def);
}
}
public byte[] getBytes(String key, byte[] def)
{
synchronized (root)
{
return XMLUtil.getGenericElementBytesValue(currentElement, TYPE_KEY, key, def);
}
}
public double getDouble(String key, double def)
{
synchronized (root)
{
return XMLUtil.getGenericElementDoubleValue(currentElement, TYPE_KEY, key, def);
}
}
public float getFloat(String key, float def)
{
synchronized (root)
{
return XMLUtil.getGenericElementFloatValue(currentElement, TYPE_KEY, key, def);
}
}
public int getInt(String key, int def)
{
synchronized (root)
{
return XMLUtil.getGenericElementIntValue(currentElement, TYPE_KEY, key, def);
}
}
public long getLong(String key, long def)
{
synchronized (root)
{
return XMLUtil.getGenericElementLongValue(currentElement, TYPE_KEY, key, def);
}
}
public void put(String key, String value)
{
synchronized (root)
{
XMLUtil.setGenericElementValue(currentElement, TYPE_KEY, key, value);
}
}
public void putBoolean(String key, boolean value)
{
synchronized (root)
{
XMLUtil.setGenericElementBooleanValue(currentElement, TYPE_KEY, key, value);
}
}
public void putBytes(String key, byte[] value)
{
synchronized (root)
{
XMLUtil.setGenericElementBytesValue(currentElement, TYPE_KEY, key, value.clone());
}
}
public void putDouble(String key, double value)
{
synchronized (root)
{
XMLUtil.setGenericElementDoubleValue(currentElement, TYPE_KEY, key, value);
}
}
public void putFloat(String key, float value)
{
synchronized (root)
{
XMLUtil.setGenericElementFloatValue(currentElement, TYPE_KEY, key, value);
}
}
public void putInt(String key, int value)
{
synchronized (root)
{
XMLUtil.setGenericElementIntValue(currentElement, TYPE_KEY, key, value);
}
}
public void putLong(String key, long value)
{
synchronized (root)
{
XMLUtil.setGenericElementLongValue(currentElement, TYPE_KEY, key, value);
}
}
}