/*
* Created on 20.2.2003
*
* To change this generated comment go to
* Window>Preferences>Java>Code Generation>Code Template
*/
package com.idega.util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Vector;
/**
* Title: SortedProperties
* Description: A class to read and write property files but stores them ordered by key in the property file.
* Copyright: (C) 2002 idega software All Rights Reserved.
* Company: idega software
* @author <a href="mailto:tryggvi@idega.is">Tryggvi Larusson</a>
* @version 1.0
*/
public class SortedProperties extends Properties implements SortedMap
{
private SortedMap internalSortedMap;
/**
*
*/
public SortedProperties()
{
super();
}
/**
* @param defaults
*/
public SortedProperties(Properties defaults)
{
super(defaults);
}
/* (non-Javadoc)
* @see java.util.SortedMap#comparator()
*/
private SortedMap getInternalSortedMap(){
if(this.internalSortedMap==null){
this.internalSortedMap=new TreeMap();
}
return this.internalSortedMap;
}
public Comparator comparator()
{
return getInternalSortedMap().comparator();
}
/* (non-Javadoc)
* @see java.util.SortedMap#subMap(java.lang.Object, java.lang.Object)
*/
public SortedMap subMap(Object fromKey, Object toKey)
{
return getInternalSortedMap().subMap(fromKey,toKey);
}
/* (non-Javadoc)
* @see java.util.SortedMap#headMap(java.lang.Object)
*/
public SortedMap headMap(Object toKey)
{
return getInternalSortedMap().headMap(toKey);
}
/* (non-Javadoc)
* @see java.util.SortedMap#tailMap(java.lang.Object)
*/
public SortedMap tailMap(Object fromKey)
{
return getInternalSortedMap().tailMap(fromKey);
}
/* (non-Javadoc)
* @see java.util.SortedMap#firstKey()
*/
public Object firstKey()
{
return getInternalSortedMap().firstKey();
}
/* (non-Javadoc)
* @see java.util.SortedMap#lastKey()
*/
public Object lastKey()
{
return getInternalSortedMap().lastKey();
}
/* (non-Javadoc)
* @see java.util.Map#clear()
*/
public synchronized void clear()
{
getInternalSortedMap().clear();
}
/* (non-Javadoc)
* @see java.util.Hashtable#contains(java.lang.Object)
*/
public synchronized boolean contains(Object value)
{
//return super.contains(value);
return getInternalSortedMap().containsValue(value);
}
/* (non-Javadoc)
* @see java.util.Map#containsKey(java.lang.Object)
*/
public synchronized boolean containsKey(Object key)
{
return getInternalSortedMap().containsKey(key);
}
/* (non-Javadoc)
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value)
{
return getInternalSortedMap().containsValue(value);
}
/* (non-Javadoc)
* @see java.util.Dictionary#elements()
*/
public synchronized Enumeration elements()
{
Vector v = new Vector(getInternalSortedMap().values());
return v.elements();
//return getInternalSortedMap().elements();
}
/* (non-Javadoc)
* @see java.util.Map#entrySet()
*/
public Set entrySet()
{
return getInternalSortedMap().entrySet();
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public synchronized boolean equals(Object o)
{
return getInternalSortedMap().equals(o);
}
public String getProperty(String key){
return (String)get(key);
}
/* (non-Javadoc)
* @see java.util.Dictionary#get(java.lang.Object)
*/
public synchronized Object get(Object key)
{
return getInternalSortedMap().get(key);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public synchronized int hashCode()
{
return getInternalSortedMap().hashCode();
}
/* (non-Javadoc)
* @see java.util.Dictionary#isEmpty()
*/
public boolean isEmpty()
{
return getInternalSortedMap().isEmpty();
}
/* (non-Javadoc)
* @see java.util.Dictionary#keys()
*/
public synchronized Enumeration keys()
{
Vector v = new Vector(keySet());
return v.elements();
}
/* (non-Javadoc)
* @see java.util.Map#keySet()
*/
public Set keySet()
{
return getInternalSortedMap().keySet();
}
/* (non-Javadoc)
* @see java.util.Dictionary#put(java.lang.Object, java.lang.Object)
*/
public synchronized Object put(Object key, Object value)
{
return getInternalSortedMap().put(key, value);
}
/* (non-Javadoc)
* @see java.util.Map#putAll(java.util.Map)
*/
public synchronized void putAll(Map t)
{
getInternalSortedMap().putAll(t);
}
/* (non-Javadoc)
* @see java.util.Hashtable#rehash()
*/
protected void rehash()
{
//getInternalSortedMap().rehash();
}
/* (non-Javadoc)
* @see java.util.Dictionary#remove(java.lang.Object)
*/
public synchronized Object remove(Object key)
{
return getInternalSortedMap().remove(key);
}
/* (non-Javadoc)
* @see java.util.Dictionary#size()
*/
public int size()
{
return getInternalSortedMap().size();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public synchronized String toString()
{
return getInternalSortedMap().toString();
}
/* (non-Javadoc)
* @see java.util.Map#values()
*/
public Collection values()
{
return getInternalSortedMap().values();
}
//BORROWED CODE FROM THE JDK LIBRARY java.util.Properties
private static final String specialSaveChars = "=: \t\r\n\f#!";
private boolean addDateHeader=false;
/**
* ATTENTION THIS METHOD NOW USES UTF-8 NOT ISO 8859-1 LIKE THE JAVADOC SAYS
* <p>
* Writes this property list (key and element pairs) in this
* <code>Properties</code> table to the output stream in a format suitable
* for loading into a <code>Properties</code> table using the
* <code>load</code> method.
* The stream is written using the ISO 8859-1 character encoding.
* <p>
* Properties from the defaults table of this <code>Properties</code>
* table (if any) are <i>not</i> written out by this method.
* <p>
* If the header argument is not null, then an ASCII <code>#</code>
* character, the header string, and a line separator are first written
* to the output stream. Thus, the <code>header</code> can serve as an
* identifying comment.
* <p>
* Next, a comment line is always written, consisting of an ASCII
* <code>#</code> character, the current date and time (as if produced
* by the <code>toString</code> method of <code>Date</code> for the
* current time), and a line separator as generated by the Writer.
* <p>
* Then every entry in this <code>Properties</code> table is written out,
* one per line. For each entry the key string is written, then an ASCII
* <code>=</code>, then the associated element string. Each character of
* the element string is examined to see whether it should be rendered as
* an escape sequence. The ASCII characters <code>\</code>, tab, newline,
* and carriage return are written as <code>\\</code>, <code>\t</code>,
* <code>\n</code>, and <code>\r</code>, respectively. Characters less
* than <code>\u0020</code> and characters greater than
* <code>\u007E</code> are written as <code>\u</code><i>xxxx</i> for
* the appropriate hexadecimal value <i>xxxx</i>. Leading space characters,
* but not embedded or trailing space characters, are written with a
* preceding <code>\</code>. The key and value characters <code>#</code>,
* <code>!</code>, <code>=</code>, and <code>:</code> are written with a
* preceding slash to ensure that they are properly loaded.
* <p>
* After the entries have been written, the output stream is flushed. The
* output stream remains open after this method returns.
*
* @param out an output stream.
* @param header a description of the property list.
* @exception IOException if writing this property list to the specified
* output stream throws an <tt>IOException</tt>.
* @exception ClassCastException if this <code>Properties</code> object
* contains any keys or values that are not <code>Strings</code>.
* @exception NullPointerException if <code>out</code> is null.
* @since 1.2
*/
public synchronized void store(OutputStream out, String header)
throws IOException
{
BufferedWriter awriter;
//awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
//TODO change javadoc comment
awriter = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
if (header != null) {
writeln(awriter, "#" + header);
}
if(getAddDateHeader()) {
writeln(awriter, "#" + new Date().toString());
}
for (Enumeration e = keys(); e.hasMoreElements();) {
String key = (String)e.nextElement();
String val = (String)get(key);
key = saveConvert(key, true);
/* No need to escape embedded and trailing spaces for value, hence
* pass false to flag.
*/
val = saveConvert(val, false);
writeln(awriter, key + "=" + val);
}
awriter.flush();
}
/**
* Gets if to add the date header to the file in store().
* Default value is false.
*/
public boolean getAddDateHeader(){
return this.addDateHeader;
}
/**
* Sets if to add the date header to the file in store().
* Default value is false.
*/
public void setAddDateHeader(boolean addHeader){
this.addDateHeader=addHeader;
}
private static void writeln(BufferedWriter bw, String s) throws IOException {
bw.write(s);
bw.newLine();
}
/*
* Converts unicodes to encoded \uxxxx
* and writes out any of the characters in specialSaveChars
* with a preceding slash
*/
private String saveConvert(String theString, boolean escapeSpace) {
if(theString == null) {theString="";}
int len = theString.length();
StringBuffer outBuffer = new StringBuffer(len*2);
for(int x=0; x<len; x++) {
char aChar = theString.charAt(x);
switch(aChar) {
case ' ':
if (x == 0 || escapeSpace) {
outBuffer.append('\\');
}
outBuffer.append(' ');
break;
case '\\':outBuffer.append('\\'); outBuffer.append('\\');
break;
case '\t':outBuffer.append('\\'); outBuffer.append('t');
break;
case '\n':outBuffer.append('\\'); outBuffer.append('n');
break;
case '\r':outBuffer.append('\\'); outBuffer.append('r');
break;
case '\f':outBuffer.append('\\'); outBuffer.append('f');
break;
default:
if ((aChar < 0x0020) || (aChar > 0x007e)) {
outBuffer.append('\\');
outBuffer.append('u');
outBuffer.append(toHex((aChar >> 12) & 0xF));
outBuffer.append(toHex((aChar >> 8) & 0xF));
outBuffer.append(toHex((aChar >> 4) & 0xF));
outBuffer.append(toHex( aChar & 0xF));
} else {
if (specialSaveChars.indexOf(aChar) != -1) {
outBuffer.append('\\');
}
outBuffer.append(aChar);
}
}
}
return outBuffer.toString();
}
/**
* Convert a nibble to a hex character
* @param nibble the nibble to convert.
*/
private static char toHex(int nibble) {
return hexDigit[(nibble & 0xF)];
}
/** A table of hex digits */
private static final char[] hexDigit = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
}