/*\
* Copyright 2006 Klaus Rogall, Hamburg, Germany (klaus.rogall@web.de). All rights reserved.
* _____________________________________________________________________________________________________________________
*
* This class is "Open Source" as defined by the Open Source Initiative (OSI). You can redistribute it and/or modify it
* under the terms of the BSD License. The license text is appended to the end of this file.
\*/
package de.klaro.base.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
/**
* Diese Klasse erweitert Properties um die Mglichkeit, hierarchisch organisierte Namensrume zu verwalten.
*
* Property-Schlssel knnen wie Pfade strukturiert sein, wobei ein Punkt als Trennzeichen erwartet wird. Auf diese
* Weise werden Properties hierarchisch gruppiert. Eine Gruppe von Properties wird 'Section' bezeichnet. Sections knnen
* Unter-Sections haben usw., die jeweils durch die Punkte getrennt werden. Alle Properties, die zu einer Section
* gehren, knnen als separate Properties weiterverarbeitet werden.
*
* Beispiel: aus den folgenden vier Properties
*
* user1.first-name = Klaus
* user1.last-name = Rogall
*
* user2.first-name = James
* user2.last-name = Gosling
*
* knnen neue Properties fr die Section "user1" erzeugt werden, die dann jeweils aus den folgenden zwei Properties
* bestehen (gleiches gilt analog for "user2"); wichtig dabei ist, dass der Section-Name nicht mehr zum Schlssel
* gehrt:
*
* first-name = Klaus
* last-name = Rogall
*
* Die Strukturierung mit hierarchisch organisierten Namensrumen bietet wahlweise auch die Mglichkeit, Default-Werte
* fr Properties zu definieren.
*
* Um den Mechanismus der Default-Werte zu erlutern, wird in folgendem Beispiel von in drei Sections "user1", "user2"
* und "user3" organisierten Properties ausgegangen werden:
*
* user1.first-name = Klaus
* user1.last-name = Rogall
* user1.ide = Eclipse
* user1.os = Windows
*
* user2.first-name = James
* user2.last-name = Gosling
* user2.ide = Netbeans
*
* user3.first-name = Dale
* user3.last-name = Fuller
* *.ide = JBuilder
* *.os = MacOS
*
* Jede der drei Sections besteht 'prinzipiell' aus vier Properties, die jedoch nicht alle explizit vorhanden sind. Die
* Default-Regel funktioniert so, dass fr den Fall einer nicht explizit definierten Property (z.B. "user3.ide")
* von vorne beginnend die Sections durch Sterne ersetzt werden, woraus sich die Default-Properties (z.B. "*.ide")
* ergeben.
*/
public class HierarchicalProperties extends ResourceProperties
{
/* ______________________________________________________________________________________________________________ *\
\* Konstanten */
/* ______________________________________________________________________________________________________________ *\
\* Klassenvariablen */
/* ______________________________________________________________________________________________________________ *\
\* Instanzvariablen */
private Properties defaults$;
/* ______________________________________________________________________________________________________________ *\
\* Konstruktoren */
/**
* Erzeugt neue, leere Properties.
*/
public HierarchicalProperties()
{
super();
defaults$ = null;
}
/**
* Erzeugt Properties mit den angegebenen Default-Properties
*
* @param defaults Die Default-Properties
* @see java.util.Properties
*/
public HierarchicalProperties(Properties defaults)
{
super(defaults);
defaults$ = defaults;
}
/**
* Erzeugt Properties, die aus der angegebenen Datei gelesen werden.
*
* @param file Die Datei
* @throws FileNotFoundException Die Datei existiert nicht
* @throws IOException Die Datei kann nicht geffnet oder gelesen werden
*/
public HierarchicalProperties(File file) throws FileNotFoundException,IOException
{
super(file);
defaults$ = null;
}
/**
* Erzeugt Properties, die aus der angegebenen Stream geladen werden.
*
* Der Stream wird nach dem Laden der Properties geschlossen.
*
* @param stream Der Stream
* @throws IOException Der Stream kann nicht gelesen werden
*/
public HierarchicalProperties(InputStream stream) throws IOException
{
super(stream);
defaults$ = null;
}
/**
* Erzeugt Properties, die aus der angegebenen Ressource geladen werden.
*
* Die Bedeutung der anzugebenden Ressource ist in der Superklasse beschrieben.
*
* @param resource Der Name der Ressource
* @throws IOException Die Ressource kann nicht geffnet oder gelesen werden
* @see de.klaro.base.util.ResourceProperties
*/
public HierarchicalProperties(String resource) throws IOException
{
super(resource);
defaults$ = null;
}
/**
* Erzeugt Properties, die aus der Klassen-Properties der angegebenen Klasse geladen werden.
*
* Die Bedeutung der Klasse ist in der Superklasse beschrieben.
*
* @param clazz Die Klasse
* @throws IOException Die Klassen-Properties knnen nicht geffnet oder gelesen werden
* @see de.klaro.base.util.ResourceProperties
*/
public HierarchicalProperties(Class<?> clazz) throws IOException
{
super(clazz);
defaults$ = null;
}
/* ______________________________________________________________________________________________________________ *\
\* Instanzmethoden */
/**
* Liefert alle Properties, die zu einer Section gehren.
*
* Der Section-Name wird aus dem Schlssel aller Properties entfernt.
*
* @param section Der Section-Name
* @return Die Properties dieser Section
*/
public synchronized HierarchicalProperties getSection(String section)
{
int index = section.indexOf('.');
if (index > 0)
return getSection(section.substring(0,index)).getSection(section.substring(index + 1));
String prefix = section + ".";
int prefixLength = prefix.length();
//
// Aus den Default-Properties muss (wenn vorhanden) die Section ebenfalls herausgefiltert werden
//
Properties defaultProperties;
if (defaults$ == null)
{
defaultProperties = null;
}
else
{
HierarchicalProperties tempProperties = new HierarchicalProperties();
tempProperties.addAll(defaults$);
defaultProperties = tempProperties.getSection(section);
}
//
// Ergebnis erzeugen
//
HierarchicalProperties properties = new HierarchicalProperties(defaultProperties);
properties.setFailOnMissingProperty(isFailOnMissingProperty());
Iterator<Object> iter;
iter = keySet().iterator();
while (iter.hasNext())
{
String key = (String)iter.next();
if (key.startsWith("*."))
properties.put(key.substring(2),getProperty(key));
}
iter = keySet().iterator();
while (iter.hasNext())
{
String key = (String)iter.next();
if (key.startsWith(prefix))
properties.put(key.substring(prefixLength),getProperty(key));
}
return properties;
}
/**
* Fgt die angegebenen Properties als Section hinzu.
*
* Die Schlssel der hinzuzufgenden Properties werden um den Section-Namen ergnzt. Eventuell vorhandene
* Properties-Eintrge werden berschrieben.
*
* @param section Der Section-Name
* @param properties Die hinzuzufgenden Properties
*/
public synchronized void addSection(String section,Properties properties)
{
String prefix = section + ".";
Enumeration<Object> enumeration = properties.keys();
while (enumeration.hasMoreElements())
{
String key = (String)enumeration.nextElement();
put(prefix + key,properties.getProperty(key));
}
}
/**
* Lscht alle Properties, die zu einer Section gehren.
*
* @param section Der Section-Name
*/
public synchronized void removeSection(String section)
{
String prefix = section + ".";
Enumeration<Object> enumeration = keys();
while (enumeration.hasMoreElements())
{
String key = (String)enumeration.nextElement();
if (key.startsWith(prefix))
remove(key);
}
}
/**
* Liefert eine Property, die unter einer angegebenen Section zu suchen ist.
*
* Es kann angegeben werden, oder der Default-Mechanismus benutzt werden soll, wenn die Properties nicht existiert.
*
* Der Default-Mechanismus ersetzt den Section-Namen von vorne beginnedn durch "*" und fhrt die Suche nach der
* Property dann nochmals durch.
*
* @param section Der Section-Name
* @param key Der Property-Schlssel innerhalb der Section
* @param findDefaults Die Default-Regel soll angewendet werden
* @return Der Property-Wert, oder 'null' falls kein Wert gefunden werden konnte
*/
public synchronized String getProperty(String section,String key,boolean findDefaults)
{
String prefix = section + ".";
String value = getProperty(prefix + key,(String)null,null);
if (value == null && findDefaults)
{
String wildcards = "";
int index = -1;
while (value == null)
{
if ((index = prefix.indexOf('.',index + 1)) < 0)
break;
wildcards += "*.";
value = getProperty(wildcards + prefix.substring(index + 1) + key,(String)null,null);
}
}
return value;
}
/**
* Fgt einen Property-Wert unter einer angegebenen Section hinzu.
*
* @param section Der Section-Name
* @param key Der Property-Schlssel
* @param value Der Property-Wert
* @return Der zuvor unter dem Schlssel gespeichert Property-Wert, oder 'null' falls es diesen nicht gab
*/
public synchronized String putProperty(String section,String key,String value)
{
return (String)put(section + "." + key,value);
}
/**
* Lscht einen Property-Wert unter einer angegebenen Section.
*
* @param section Der Section-Name
* @param key Der Property-Schlssel
* @return Der zuvor unter dem Schlssel gespeichert Property-Wert, oder 'null' falls es diesen nicht gab
*/
public synchronized String removeProperty(String section,String key)
{
return (String)remove(section + "." + key);
}
/* ______________________________________________________________________________________________________________ *\
\* Klassenmethoden */
/* ______________________________________________________________________________________________________________ *\
\* Klassen */
}
/*\
* _____________________________________________________________________________________________________________________
*
* This software is distributed under the terms of the BSD License:
*
* Copyright 2006 Klaus Rogall, Hamburg, Germany (klaus.rogall@web.de). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* - Neither the name of the Klaus Rogall nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* _____________________________________________________________________________________________________________________
\*/