Author: nextgens
Date: 2006-08-13 22:14:40 +0000 (Sun, 13 Aug 2006)
New Revision: 10071
Added:
trunk/apps/blueBunny/src/freenet/config/
trunk/apps/blueBunny/src/freenet/config/BooleanCallback.java
trunk/apps/blueBunny/src/freenet/config/BooleanOption.java
trunk/apps/blueBunny/src/freenet/config/Config.java
trunk/apps/blueBunny/src/freenet/config/FilePersistentConfig.java
trunk/apps/blueBunny/src/freenet/config/IntCallback.java
trunk/apps/blueBunny/src/freenet/config/IntOption.java
trunk/apps/blueBunny/src/freenet/config/InvalidConfigValueException.java
trunk/apps/blueBunny/src/freenet/config/LongCallback.java
trunk/apps/blueBunny/src/freenet/config/LongOption.java
trunk/apps/blueBunny/src/freenet/config/Option.java
trunk/apps/blueBunny/src/freenet/config/OptionFormatException.java
trunk/apps/blueBunny/src/freenet/config/ShortCallback.java
trunk/apps/blueBunny/src/freenet/config/ShortOption.java
trunk/apps/blueBunny/src/freenet/config/StringArrCallback.java
trunk/apps/blueBunny/src/freenet/config/StringArrOption.java
trunk/apps/blueBunny/src/freenet/config/StringCallback.java
trunk/apps/blueBunny/src/freenet/config/StringOption.java
trunk/apps/blueBunny/src/freenet/config/SubConfig.java
trunk/apps/blueBunny/src/freenet/support/
trunk/apps/blueBunny/src/freenet/support/Fields.java
trunk/apps/blueBunny/src/freenet/support/LineReader.java
trunk/apps/blueBunny/src/freenet/support/SimpleFieldSet.java
trunk/apps/blueBunny/src/freenet/support/URLDecoder.java
trunk/apps/blueBunny/src/freenet/support/URLEncodedFormatException.java
trunk/apps/blueBunny/src/freenet/support/URLEncoder.java
trunk/apps/blueBunny/src/freenet/support/io/
trunk/apps/blueBunny/src/freenet/support/io/LineReader.java
trunk/apps/blueBunny/src/freenet/support/io/LineReadingInputStream.java
trunk/apps/blueBunny/src/freenet/support/io/TooLongException.java
Modified:
trunk/apps/blueBunny/src/freenet/systray/Systray.java
Log:
blueBunny: import some code from the node to handle the configuration properly
Added: trunk/apps/blueBunny/src/freenet/config/BooleanCallback.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/BooleanCallback.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/BooleanCallback.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,22 @@
+package freenet.config;
+
+/**
+ * A callback to be called when a config value of integer type changes.
+ * Also reports the current value.
+ */
+public interface BooleanCallback {
+
+ /**
+ * Get the current, used value of the config variable.
+ */
+ boolean get();
+
+ /**
+ * Set the config variable to a new value.
+ * @param val The new value.
+ * @throws InvalidConfigOptionException If the new value is invalid for
+ * this particular option.
+ */
+ void set(boolean val) throws InvalidConfigValueException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/BooleanOption.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/BooleanOption.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/BooleanOption.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,52 @@
+package freenet.config;
+
+public class BooleanOption extends Option {
+
+ final boolean defaultValue;
+ final BooleanCallback cb;
+ private boolean currentValue;
+
+ public BooleanOption(SubConfig conf, String optionName, boolean
defaultValue, int sortOrder,
+ boolean expert, String shortDesc, String longDesc,
BooleanCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = defaultValue;
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ }
+
+ /** Get the current value. This is the value in use if we have finished
+ * initialization, otherwise it is the value set at startup (possibly
the default). */
+ public boolean getValue() {
+ if(config.hasFinishedInitialization())
+ return currentValue = cb.get();
+ else return currentValue;
+ }
+
+ public void setValue(String val) throws InvalidConfigValueException {
+ if(val.equalsIgnoreCase("true") || val.equalsIgnoreCase("yes"))
{
+ set(true);
+ } else if(val.equalsIgnoreCase("false") ||
val.equalsIgnoreCase("no")) {
+ set(false);
+ } else
+ throw new OptionFormatException("Unrecognized boolean:
"+val);
+ }
+
+ public void set(boolean b) throws InvalidConfigValueException {
+ cb.set(b);
+ currentValue = b;
+ }
+
+ public String getValueString() {
+ return Boolean.toString(getValue());
+ }
+
+ public void setInitialValue(String val) throws
InvalidConfigValueException {
+ if(val.equalsIgnoreCase("true") || val.equalsIgnoreCase("yes"))
{
+ currentValue = true;
+ } else if(val.equalsIgnoreCase("false") ||
val.equalsIgnoreCase("no")) {
+ currentValue = false;
+ } else
+ throw new OptionFormatException("Unrecognized boolean:
"+val);
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/Config.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/Config.java 2006-08-13 22:01:32 UTC
(rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/Config.java 2006-08-13 22:14:40 UTC
(rev 10071)
@@ -0,0 +1,48 @@
+package freenet.config;
+
+import java.util.HashMap;
+
+/** Global configuration object for a node. SubConfig's register here.
+ * Handles writing to a file etc.
+ */
+public class Config {
+
+ protected final HashMap configsByPrefix;
+
+ public Config() {
+ configsByPrefix = new HashMap();
+ }
+
+ public void register(SubConfig sc) {
+ synchronized(this) {
+ if(configsByPrefix.containsKey(sc.prefix))
+ throw new IllegalArgumentException("Already
registered "+sc.prefix+": "+sc);
+ configsByPrefix.put(sc.prefix, sc);
+ }
+ }
+
+ /** Write current config to disk
+ * @throws IOException */
+ public void store() {
+ // Do nothing
+ }
+
+ /** Finished initialization */
+ public void finishedInit() {
+ // Do nothing
+ }
+
+ public void onRegister(SubConfig config, Option o) {
+ // Do nothing
+ }
+
+ /** Fetch all the SubConfig's. Used by user-facing config thingies. */
+ public synchronized SubConfig[] getConfigs() {
+ return (SubConfig[]) configsByPrefix.values().toArray(new
SubConfig[configsByPrefix.size()]);
+ }
+
+ public synchronized SubConfig get(String subConfig){
+ return (SubConfig)configsByPrefix.get(subConfig);
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/FilePersistentConfig.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/FilePersistentConfig.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/FilePersistentConfig.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,160 @@
+package freenet.config;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Iterator;
+
+import freenet.support.SimpleFieldSet;
+import freenet.support.io.LineReadingInputStream;
+
+/**
+ * Global Config object which persists to a file.
+ *
+ * Reads the config file into a SimpleFieldSet when created.
+ * During init, SubConfig's are registered, and fed the relevant parts of the
SFS.
+ * Once initialization has finished, we check whether there are any options
remaining.
+ * If so, we complain about them.
+ * And then we write the config file back out.
+ */
+public class FilePersistentConfig extends Config {
+
+ final File filename;
+ final File tempFilename;
+ private SimpleFieldSet origConfigFileContents;
+
+ public FilePersistentConfig(File f) throws IOException {
+ this.filename = f;
+ this.tempFilename = new File(f.getPath()+".tmp");
+ boolean filenameExists = f.exists();
+ boolean tempFilenameExists = tempFilename.exists();
+ if(filenameExists && !filename.canWrite())
+ System.err.println("Warning: Cannot write to config
file: "+filename);
+ if(tempFilenameExists && !tempFilename.canWrite())
+ System.err.println("Warning: Cannot write to config
tempfile: "+tempFilename);
+ if(filenameExists) {
+ if(f.canRead()) {
+ try {
+ initialLoad(filename);
+ return;
+ } catch (FileNotFoundException e) {
+ System.err.println("No config file
found, creating new: "+f);
+ } catch (EOFException e) {
+ System.err.println("Empty config file
"+f);
+ }
+ // Other IOE's indicate a more serious problem.
+ } else {
+ throw new IOException("Cannot read config
file");
+ }
+ }
+ if(tempFilename.exists()) {
+ if(tempFilename.canRead()) {
+ try {
+ initialLoad(tempFilename);
+ } catch (FileNotFoundException e) {
+ System.err.println("No config file
found, creating new: "+f);
+ } // Other IOE's indicate a more serious
problem.
+ } else {
+ throw new IOException("Cannot read config
file");
+ }
+ } else {
+ System.err.println("No config file found, creating new:
"+f);
+ }
+ }
+
+ /** Load the config file into a SimpleFieldSet.
+ * @throws IOException */
+ private void initialLoad(File toRead) throws IOException {
+ FileInputStream fis = new FileInputStream(toRead);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ try {
+ LineReadingInputStream lis = new
LineReadingInputStream(bis);
+ // Config file is UTF-8 too!
+ synchronized (this) {
+ origConfigFileContents = new
SimpleFieldSet(lis, 32768, 128, true, true);
+ }
+ } finally {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ System.err.println("Could not close
"+filename+": "+e);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void register(SubConfig sc) {
+ super.register(sc);
+ }
+
+ /**
+ * Finished initialization. So any remaining options must be invalid.
+ */
+ public synchronized void finishedInit() {
+ if(origConfigFileContents == null) return;
+ Iterator i = origConfigFileContents.keyIterator();
+ while(i.hasNext()) {
+ String key = (String) i.next();
+ System.err.println("Unknown option: "+key+"
(value="+origConfigFileContents.get(key)+")");
+ }
+ }
+
+ public void store() {
+ try {
+ innerStore();
+ } catch (IOException e) {
+ String err = "Cannot store config: "+e;
+ System.err.println(err);
+ e.printStackTrace();
+ }
+ }
+
+ public void innerStore() throws IOException {
+ SimpleFieldSet fs = exportFieldSet();
+ FileOutputStream fos = new FileOutputStream(tempFilename);
+ BufferedWriter bw = new BufferedWriter(new
OutputStreamWriter(fos, "UTF-8"));
+ synchronized(this) {
+ fs.writeTo(bw);
+ }
+ bw.close();
+ if(!tempFilename.renameTo(filename)) {
+ filename.delete();
+ tempFilename.renameTo(filename);
+ }
+ }
+
+ private synchronized SimpleFieldSet exportFieldSet() {
+ SimpleFieldSet fs = new SimpleFieldSet();
+ SubConfig[] configs;
+ synchronized(this) {
+ configs = (SubConfig[])
configsByPrefix.values().toArray(new SubConfig[configsByPrefix.size()]);
+ }
+ for(int i=0;i<configs.length;i++) {
+ SimpleFieldSet scfs = configs[i].exportFieldSet();
+ fs.put(configs[i].prefix, scfs);
+ }
+ return fs;
+ }
+
+ public void onRegister(SubConfig config, Option o) {
+ String val, name;
+ synchronized(this) {
+ if(origConfigFileContents == null) return;
+ name =
config.prefix+SimpleFieldSet.MULTI_LEVEL_CHAR+o.name;
+ val = origConfigFileContents.get(name);
+ origConfigFileContents.removeValue(name);
+ if(val == null) return;
+ }
+ try {
+ o.setInitialValue(val);
+ } catch (InvalidConfigValueException e) {
+ System.err.println("Could not parse config option
"+name+": "+e);
+ }
+ }
+}
Added: trunk/apps/blueBunny/src/freenet/config/IntCallback.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/IntCallback.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/IntCallback.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,22 @@
+package freenet.config;
+
+/**
+ * A callback to be called when a config value of integer type changes.
+ * Also reports the current value.
+ */
+public interface IntCallback {
+
+ /**
+ * Get the current, used value of the config variable.
+ */
+ int get();
+
+ /**
+ * Set the config variable to a new value.
+ * @param val The new value.
+ * @throws InvalidConfigOptionException If the new value is invalid for
+ * this particular option.
+ */
+ void set(int val) throws InvalidConfigValueException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/IntOption.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/IntOption.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/IntOption.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,63 @@
+package freenet.config;
+
+import freenet.support.Fields;
+
+/** Integer config variable */
+public class IntOption extends Option {
+
+ final int defaultValue;
+ final IntCallback cb;
+ private int currentValue;
+ // Cache it mostly so that we can keep SI units
+ private String cachedStringValue;
+
+ public IntOption(SubConfig conf, String optionName, int defaultValue,
String defaultValueString,
+ int sortOrder, boolean expert, String shortDesc, String
longDesc, IntCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = defaultValue;
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ this.cachedStringValue = defaultValueString;
+ }
+
+ public IntOption(SubConfig conf, String optionName, String
defaultValueString,
+ int sortOrder, boolean expert, String shortDesc, String
longDesc, IntCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = Fields.parseInt(defaultValueString);
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ this.cachedStringValue = defaultValueString;
+ }
+
+ /** Get the current value. This is the value in use if we have finished
+ * initialization, otherwise it is the value set at startup (possibly
the default). */
+ public int getValue() {
+ if(config.hasFinishedInitialization()) {
+ int val = cb.get();
+ if(currentValue != val) {
+ currentValue = val;
+ cachedStringValue = null;
+ }
+ }
+ return currentValue;
+ }
+
+ public void setValue(String val) throws InvalidConfigValueException {
+ int x = Fields.parseInt(val);
+ cb.set(x);
+ cachedStringValue = val;
+ currentValue = x;
+ }
+
+ public void setInitialValue(String val) {
+ int x = Fields.parseInt(val);
+ cachedStringValue = val;
+ currentValue = x;
+ }
+
+ public String getValueString() {
+ if(cachedStringValue != null) return cachedStringValue;
+ return Integer.toString(getValue());
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/InvalidConfigValueException.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/InvalidConfigValueException.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/InvalidConfigValueException.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,17 @@
+package freenet.config;
+
+/**
+ * Thrown when the node refuses to set a config variable to a particular
+ * value because it is invalid. Just because this is not thrown does not
+ * necessarily mean that there are no problems with the value defined,
+ * it merely means that there are no immediately detectable problems with
+ * it.
+ */
+public class InvalidConfigValueException extends Exception {
+ private static final long serialVersionUID = -1;
+
+ public InvalidConfigValueException(String msg) {
+ super(msg);
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/LongCallback.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/LongCallback.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/LongCallback.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,22 @@
+package freenet.config;
+
+/**
+ * A callback to be called when a config value of long type changes.
+ * Also reports the current value.
+ */
+public interface LongCallback {
+
+ /**
+ * Get the current, used value of the config variable.
+ */
+ long get();
+
+ /**
+ * Set the config variable to a new value.
+ * @param val The new value.
+ * @throws InvalidConfigOptionException If the new value is invalid for
+ * this particular option.
+ */
+ void set(long val) throws InvalidConfigValueException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/LongOption.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/LongOption.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/LongOption.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,63 @@
+package freenet.config;
+
+import freenet.support.Fields;
+
+/** Long config variable */
+public class LongOption extends Option {
+
+ final long defaultValue;
+ final LongCallback cb;
+ private long currentValue;
+ // Cache it mostly so that we can keep SI units
+ private String cachedStringValue;
+
+ public LongOption(SubConfig conf, String optionName, long defaultValue,
String defaultValueString,
+ int sortOrder, boolean expert, String shortDesc, String
longDesc, LongCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = defaultValue;
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ this.cachedStringValue = defaultValueString;
+ }
+
+ public LongOption(SubConfig conf, String optionName, String
defaultValueString,
+ int sortOrder, boolean expert, String shortDesc, String
longDesc, LongCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = Fields.parseLong(defaultValueString);
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ this.cachedStringValue = defaultValueString;
+ }
+
+ /** Get the current value. This is the value in use if we have finished
+ * initialization, otherwise it is the value set at startup (possibly
the default). */
+ public long getValue() {
+ if(config.hasFinishedInitialization()) {
+ long val = cb.get();
+ if(currentValue != val) {
+ currentValue = val;
+ cachedStringValue = null;
+ }
+ }
+ return currentValue;
+ }
+
+ public void setValue(String val) throws InvalidConfigValueException {
+ long x = Fields.parseLong(val);
+ cb.set(x);
+ cachedStringValue = val;
+ currentValue = x;
+ }
+
+ public String getValueString() {
+ if(cachedStringValue != null) return cachedStringValue;
+ return Long.toString(getValue());
+ }
+
+ public void setInitialValue(String val) throws
InvalidConfigValueException {
+ long x = Fields.parseLong(val);
+ cachedStringValue = val;
+ currentValue = x;
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/Option.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/Option.java 2006-08-13 22:01:32 UTC
(rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/Option.java 2006-08-13 22:14:40 UTC
(rev 10071)
@@ -0,0 +1,70 @@
+package freenet.config;
+
+/**
+ * A config option.
+ */
+public abstract class Option {
+
+ /** The parent SubConfig object */
+ final SubConfig config;
+ /** The option name */
+ final String name;
+ /** The sort order */
+ final int sortOrder;
+ /** Is this config variable expert-only? */
+ final boolean expert;
+ /** Short description of value e.g. "FCP port" */
+ final String shortDesc;
+ /** Long description of value e.g. "The TCP port to listen for FCP
connections on" */
+ final String longDesc;
+
+ Option(SubConfig config, String name, int sortOrder, boolean expert,
String shortDesc, String longDesc) {
+ this.config = config;
+ this.name = name;
+ this.sortOrder = sortOrder;
+ this.expert = expert;
+ this.shortDesc = shortDesc;
+ this.longDesc = longDesc;
+ }
+
+ /**
+ * Set this option's current value to a string. Will call the callback.
Does not care
+ * whether the value of the option has changed.
+ */
+ public abstract void setValue(String val) throws
InvalidConfigValueException;
+
+ /**
+ * Get the current value of the option as a string.
+ */
+ public abstract String getValueString();
+
+ /** Set to a value from the config file; this is not passed on to the
callback, as we
+ * expect the client-side initialization to check the value. The
callback is not valid
+ * until the client calls finishedInitialization().
+ * @throws InvalidConfigValueException
+ */
+ public abstract void setInitialValue(String val) throws
InvalidConfigValueException;
+
+ /**
+ * Call the callback with the current value of the option.
+ */
+ public void forceUpdate() throws InvalidConfigValueException {
+ setValue(getValueString());
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ public String getShortDesc(){
+ return shortDesc;
+ }
+
+ public String getLongDesc(){
+ return longDesc;
+ }
+
+ public boolean isExpert(){
+ return expert;
+ }
+}
Added: trunk/apps/blueBunny/src/freenet/config/OptionFormatException.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/OptionFormatException.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/OptionFormatException.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,13 @@
+package freenet.config;
+
+/**
+ * Thrown when a format error occurs, and we cannot parse the string set into
the appropriate
+ * type.
+ */
+public class OptionFormatException extends InvalidConfigValueException {
+ private static final long serialVersionUID = -1;
+ public OptionFormatException(String msg) {
+ super(msg);
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/ShortCallback.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/ShortCallback.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/ShortCallback.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,22 @@
+package freenet.config;
+
+/**
+ * A callback to be called when a config value of short type changes.
+ * Also reports the current value.
+ */
+public interface ShortCallback {
+
+ /**
+ * Get the current, used value of the config variable.
+ */
+ short get();
+
+ /**
+ * Set the config variable to a new value.
+ * @param val The new value.
+ * @throws InvalidConfigOptionException If the new value is invalid for
+ * this particular option.
+ */
+ void set(short val) throws InvalidConfigValueException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/ShortOption.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/ShortOption.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/ShortOption.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,42 @@
+package freenet.config;
+
+import freenet.support.Fields;
+
+public class ShortOption extends Option {
+
+ final short defaultValue;
+ final ShortCallback cb;
+ private short currentValue;
+
+ public ShortOption(SubConfig conf, String optionName, short
defaultValue, int sortOrder,
+ boolean expert, String shortDesc, String longDesc,
ShortCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = defaultValue;
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ }
+
+ /** Get the current value. This is the value in use if we have finished
+ * initialization, otherwise it is the value set at startup (possibly
the default). */
+ public short getValue() {
+ if(config.hasFinishedInitialization())
+ return currentValue = cb.get();
+ else return currentValue;
+ }
+
+ public void setValue(String val) throws InvalidConfigValueException {
+ short x = Fields.parseShort(val);
+ cb.set(x);
+ currentValue = x;
+ }
+
+ public String getValueString() {
+ return Short.toString(getValue());
+ }
+
+ public void setInitialValue(String val) throws
InvalidConfigValueException {
+ short x = Fields.parseShort(val);
+ currentValue = x;
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/StringArrCallback.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/StringArrCallback.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/StringArrCallback.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,19 @@
+package freenet.config;
+
+/** Callback (getter/setter) for a string config variable */
+public interface StringArrCallback {
+
+ /**
+ * Get the current, used value of the config variable.
+ */
+ String get();
+
+ /**
+ * Set the config variable to a new value.
+ * @param val The new value.
+ * @throws InvalidConfigOptionException If the new value is invalid for
+ * this particular option.
+ */
+ void set(String val) throws InvalidConfigValueException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/StringArrOption.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/StringArrOption.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/StringArrOption.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,75 @@
+package freenet.config;
+
+import freenet.support.URLDecoder;
+import freenet.support.URLEncodedFormatException;
+import freenet.support.URLEncoder;
+
+public class StringArrOption extends Option {
+
+ private final String defaultValue;
+ private final StringArrCallback cb;
+ private String currentValue;
+
+ public static final String delimiter = ";";
+
+ public StringArrOption(SubConfig conf, String optionName, String
defaultValue, int sortOrder,
+ boolean expert, String shortDesc, String longDesc,
StringArrCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = (defaultValue==null)?"":defaultValue;
+ this.cb = cb;
+ this.currentValue = (defaultValue==null)?"":defaultValue;
+ }
+
+ public StringArrOption(SubConfig conf, String optionName, String
defaultValue[], int sortOrder,
+ boolean expert, String shortDesc, String longDesc,
StringArrCallback cb) {
+ this(conf, optionName, arrayToString(defaultValue), sortOrder,
expert, shortDesc, longDesc, cb);
+ }
+
+ /** Get the current value. This is the value in use if we have finished
+ * initialization, otherwise it is the value set at startup (possibly
the default). */
+ public String[] getValue() {
+ return getValueString().split(delimiter);
+ }
+
+ public void setValue(String val) throws InvalidConfigValueException {
+ setInitialValue(val);
+ cb.set(this.currentValue);
+ }
+
+ public String getValueString() {
+ if(config.hasFinishedInitialization())
+ currentValue = cb.get();
+ return currentValue;
+ }
+
+ public void setInitialValue(String val) throws
InvalidConfigValueException {
+ this.currentValue = val;
+ }
+
+
+ public static String arrayToString(String[] arr) {
+ if (arr == null)
+ return null;
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0 ; i < arr.length ; i++)
+ sb.append(arr[i] + delimiter);
+ return sb.toString();
+ }
+
+ public static String encode(String s) {
+ return URLEncoder.encode(s);
+ }
+
+ public static String decode(String s) {
+ try {
+ return URLDecoder.decode(s);
+ } catch (URLEncodedFormatException e) {
+ return null;
+ }
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/StringCallback.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/StringCallback.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/StringCallback.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,19 @@
+package freenet.config;
+
+/** Callback (getter/setter) for a string config variable */
+public interface StringCallback {
+
+ /**
+ * Get the current, used value of the config variable.
+ */
+ String get();
+
+ /**
+ * Set the config variable to a new value.
+ * @param val The new value.
+ * @throws InvalidConfigOptionException If the new value is invalid for
+ * this particular option.
+ */
+ void set(String val) throws InvalidConfigValueException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/StringOption.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/StringOption.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/StringOption.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,38 @@
+package freenet.config;
+
+public class StringOption extends Option {
+
+ final String defaultValue;
+ final StringCallback cb;
+ private String currentValue;
+
+ public StringOption(SubConfig conf, String optionName, String
defaultValue, int sortOrder,
+ boolean expert, String shortDesc, String longDesc,
StringCallback cb) {
+ super(conf, optionName, sortOrder, expert, shortDesc, longDesc);
+ this.defaultValue = defaultValue;
+ this.cb = cb;
+ this.currentValue = defaultValue;
+ }
+
+ /** Get the current value. This is the value in use if we have finished
+ * initialization, otherwise it is the value set at startup (possibly
the default). */
+ public String getValue() {
+ if(config.hasFinishedInitialization())
+ return currentValue = cb.get();
+ else return currentValue;
+ }
+
+ public void setValue(String val) throws InvalidConfigValueException {
+ cb.set(val);
+ this.currentValue = val;
+ }
+
+ public String getValueString() {
+ return getValue();
+ }
+
+ public void setInitialValue(String val) throws
InvalidConfigValueException {
+ this.currentValue = val;
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/config/SubConfig.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/config/SubConfig.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/config/SubConfig.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,209 @@
+package freenet.config;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import freenet.support.SimpleFieldSet;
+
+/**
+ * A specific configuration block.
+ */
+public class SubConfig {
+
+ private final HashMap map;
+ public final Config config;
+ final String prefix;
+ private boolean hasInitialized;
+
+ public SubConfig(String prefix, Config config) {
+ this.config = config;
+ this.prefix = prefix;
+ map = new HashMap();
+ hasInitialized = false;
+ config.register(this);
+ }
+
+ /**
+ * Return all the options registered. Each includes its name.
+ * Used by e.g. webconfig.
+ */
+ public synchronized Option[] getOptions() {
+ return (Option[]) map.values().toArray(new Option[map.size()]);
+ }
+
+ public synchronized Option getOption(String option){
+ return (Option)map.get(option);
+ }
+
+ public void register(Option o) {
+ synchronized(this) {
+ if(o.name.indexOf(SimpleFieldSet.MULTI_LEVEL_CHAR) !=
-1)
+ throw new IllegalArgumentException("Option
names must not contain "+SimpleFieldSet.MULTI_LEVEL_CHAR);
+ if(map.containsKey(o.name))
+ throw new IllegalArgumentException("Already
registered: "+o.name+" on "+this);
+ map.put(o.name, o);
+ }
+ config.onRegister(this, o);
+ }
+
+ public void register(String optionName, int defaultValue, int sortOrder,
+ boolean expert, String shortDesc, String longDesc,
IntCallback cb) {
+ register(new IntOption(this, optionName, defaultValue, null,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, long defaultValue, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
LongCallback cb) {
+ register(new LongOption(this, optionName, defaultValue, null,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, String defaultValueString, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
IntCallback cb) {
+ register(new IntOption(this, optionName, defaultValueString,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, String defaultValueString, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
LongCallback cb) {
+ register(new LongOption(this, optionName, defaultValueString,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, boolean defaultValue, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
BooleanCallback cb) {
+ register(new BooleanOption(this, optionName, defaultValue,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, String defaultValue, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
StringCallback cb) {
+ register(new StringOption(this, optionName, defaultValue,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, short defaultValue, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
ShortCallback cb) {
+ register(new ShortOption(this, optionName, defaultValue,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public void register(String optionName, String[] defaultValue, int
sortOrder,
+ boolean expert, String shortDesc, String longDesc,
StringArrCallback cb) {
+ register(new StringArrOption(this, optionName, defaultValue,
sortOrder, expert, shortDesc, longDesc, cb));
+ }
+
+ public int getInt(String optionName) {
+ IntOption o;
+ synchronized(this) {
+ o = (IntOption) map.get(optionName);
+ }
+ return o.getValue();
+ }
+
+ public long getLong(String optionName) {
+ LongOption o;
+ synchronized(this) {
+ o = (LongOption) map.get(optionName);
+ }
+ return o.getValue();
+ }
+
+ public boolean getBoolean(String optionName) {
+ BooleanOption o;
+ synchronized(this) {
+ o = (BooleanOption) map.get(optionName);
+ }
+ return o.getValue();
+ }
+
+ public String getString(String optionName) {
+ StringOption o;
+ synchronized(this) {
+ o = (StringOption) map.get(optionName);
+ }
+ return o.getValue();
+ }
+
+ public String[] getStringArr(String optionName) {
+ StringArrOption o;
+ synchronized(this) {
+ o = (StringArrOption) map.get(optionName);
+ }
+ return o.getValue();
+ }
+
+ public short getShort(String optionName) {
+ ShortOption o;
+ synchronized(this) {
+ o = (ShortOption) map.get(optionName);
+ }
+ return o.getValue();
+ }
+
+ /**
+ * Has the object we are attached to finished initialization?
+ */
+ public boolean hasFinishedInitialization() {
+ return hasInitialized;
+ }
+
+ /**
+ * Called when the object we are attached to has finished init.
+ * After this point, the callbacks are authoritative for values of
+ * config variables, and will be called when values are changed by
+ * the user.
+ */
+ public void finishedInitialization() {
+ hasInitialized = true;
+ }
+
+ /**
+ * Set options from a SimpleFieldSet. Once we process an option, we
must remove it.
+ */
+ public void setOptions(SimpleFieldSet sfs) {
+ Set entrySet = map.entrySet();
+ Iterator i = entrySet.iterator();
+ while(i.hasNext()) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ Option o = (Option) entry.getValue();
+ String val = sfs.get(key);
+ if(val != null) {
+ try {
+ o.setValue(val);
+ } catch (InvalidConfigValueException e) {
+ String msg = "Invalid config value:
"+prefix+SimpleFieldSet.MULTI_LEVEL_CHAR+key+" = "+val+" : error: "+e;
+ System.err.println(msg); // might be
about logging?
+ }
+ }
+ }
+ }
+
+ public SimpleFieldSet exportFieldSet() {
+ SimpleFieldSet fs = new SimpleFieldSet();
+ Set entrySet = map.entrySet();
+ Iterator i = entrySet.iterator();
+ while(i.hasNext()) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ Option o = (Option) entry.getValue();
+ fs.put(key, o.getValueString());
+ }
+ return fs;
+ }
+
+ /**
+ * Force an option to be updated even if it hasn't changed.
+ * @throws InvalidConfigValueException
+ */
+ public void forceUpdate(String optionName) throws
InvalidConfigValueException {
+ Option o = (Option) map.get(optionName);
+ o.forceUpdate();
+ }
+
+ public void set(String name, String value) throws
InvalidConfigValueException {
+ Option o = (Option) map.get(name);
+ o.setValue(value);
+ }
+
+ public String getPrefix(){
+ return prefix;
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/Fields.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/Fields.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/Fields.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,620 @@
+package freenet.support;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.StringTokenizer;
+
+/**
+ * This class contains static methods used for parsing boolean and unsigned
+ * long fields in Freenet messages. Also some general utility methods for
+ * dealing with string and numeric data.
+ *
+ * @author oskar
+ */
+public abstract class Fields {
+
+ /**
+ * All possible chars for representing a number as a String. Used to
+ * optimize numberList().
+ */
+ private final static char[] digits =
+ {
+ '0',
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm',
+ 'n',
+ 'o',
+ 'p',
+ 'q',
+ 'r',
+ 's',
+ 't',
+ 'u',
+ 'v',
+ 'w',
+ 'x',
+ 'y',
+ 'z' };
+
+ /**
+ * Converts a hex string into a long. Long.parseLong(hex, 16) assumes
the
+ * input is nonnegative unless there is a preceding minus sign. This
method
+ * reads the input as twos complement instead, so if the input is 8
bytes
+ * long, it will correctly restore a negative long produced by
+ * Long.toHexString() but not neccesarily one produced by
+ * Long.toString(x,16) since that method will produce a string like
'-FF'
+ * for negative longs values.
+ *
+ * @param hex
+ * A string in capital or lower case hex, of no more then 16
+ * characters.
+ * @throws NumberFormatException
+ * if the string is more than 16 characters long, or if any
+ * character is not in the set [0-9a-fA-f]
+ */
+ public static final long hexToLong(String hex)
+ throws NumberFormatException {
+ int len = hex.length();
+ if (len > 16)
+ throw new NumberFormatException();
+
+ long l = 0;
+ for (int i = 0; i < len; i++) {
+ l <<= 4;
+ int c = Character.digit(hex.charAt(i), 16);
+ if (c < 0)
+ throw new NumberFormatException();
+ l |= c;
+ }
+ return l;
+ }
+
+ /**
+ * Converts a hex string into an int. Integer.parseInt(hex, 16) assumes
the
+ * input is nonnegative unless there is a preceding minus sign. This
method
+ * reads the input as twos complement instead, so if the input is 8
bytes
+ * long, it will correctly restore a negative int produced by
+ * Integer.toHexString() but not neccesarily one produced by
+ * Integer.toString(x,16) since that method will produce a string like
+ * '-FF' for negative integer values.
+ *
+ * @param hex
+ * A string in capital or lower case hex, of no more then 16
+ * characters.
+ * @throws NumberFormatException
+ * if the string is more than 16 characters long, or if any
+ * character is not in the set [0-9a-fA-f]
+ */
+ public static final int hexToInt(String hex) throws
NumberFormatException {
+ int len = hex.length();
+ if (len > 16)
+ throw new NumberFormatException();
+
+ int l = 0;
+ for (int i = 0; i < len; i++) {
+ l <<= 4;
+ int c = Character.digit(hex.charAt(i), 16);
+ if (c < 0)
+ throw new NumberFormatException();
+ l |= c;
+ }
+ return l;
+ }
+
+ /**
+ * Finds the boolean value of the field, by doing a caseless match with
the
+ * strings "true" and "false".
+ *
+ * @param s
+ * The string
+ * @param def
+ * The default value if the string can't be parsed. If the
+ * default is true, it checks that the string is not
"false"; if
+ * it is false, it checks whether the string is "true".
+ * @return the boolean field value or the default value if the field
value
+ * couldn't be parsed.
+ */
+ /* wooo, rocket science! (this is purely abstraction people) */
+ public static final boolean stringToBool(String s, boolean def) {
+ if(s == null) return def;
+ return (def ? !s.equalsIgnoreCase("false") :
s.equalsIgnoreCase("true"));
+ }
+
+ /**
+ * Converts a boolean to a String of either "true" or "false".
+ *
+ * @param b
+ * the boolean value to convert.
+ * @return A "true" or "false" String.
+ */
+ public static final String boolToString(boolean b) {
+ return b ? "true" : "false";
+ }
+
+ public static final String[] commaList(String ls) {
+ StringTokenizer st = new StringTokenizer(ls, ",");
+ String[] r = new String[st.countTokens()];
+ for (int i = 0; i < r.length; i++) {
+ r[i] = st.nextToken().trim();
+ }
+ return r;
+ }
+
+ public static final String commaList(String[] ls) {
+ return textList(ls, ',');
+ }
+
+ public static final String textList(String[] ls, char ch) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < ls.length; i++) {
+ sb.append(ls[i]);
+ if (i != ls.length - 1)
+ sb.append(ch);
+ }
+ return sb.toString();
+ }
+
+ public static final long[] numberList(String ls)
+ throws NumberFormatException {
+ StringTokenizer st = new StringTokenizer(ls, ",");
+ long[] r = new long[st.countTokens()];
+ for (int i = 0; i < r.length; i++) {
+ r[i] = hexToLong(st.nextToken());
+ }
+ return r;
+ }
+
+ public static final String numberList(long[] ls) {
+ char[] numberBuf = new char[64];
+ StringBuffer listBuf = new StringBuffer(ls.length * 18);
+ for (int i = 0; i < ls.length; i++) {
+
+ // Convert the number into a string in a fixed size
buffer.
+ long l = ls[i];
+ int charPos = 64;
+ do {
+ numberBuf[--charPos] = digits[(int) (l & 0x0F)];
+ l >>>= 4;
+ } while (l != 0);
+
+ listBuf.append(numberBuf, charPos, (64 - charPos));
+ if (i != ls.length - 1) {
+ listBuf.append(',');
+ }
+ }
+ return listBuf.toString();
+ }
+
+ /**
+ * Parses a time and date value, using a very strict format. The value
has
+ * to be of the form YYYYMMDD-HH:MM:SS (where seconds may include a
+ * decimal) or YYYYMMDD (in which case 00:00:00 is assumed for time).
+ * Another accepted format is +/-{integer}{day|month|year|minute|second}
+ *
+ * @return millis of the epoch of at the time described.
+ */
+ public static final long dateTime(String date)
+ throws NumberFormatException {
+
+ if (date.length() == 0)
+ throw new NumberFormatException("Date time empty");
+
+ if ((date.charAt(0) == '-') || (date.charAt(0) == '+')) {
+ // Relative date
+ StringBuffer sb = new StringBuffer(10);
+ for (int x = 1; x < date.length(); x++) {
+ char c = date.charAt(x);
+ if (Character.isDigit(c)) {
+ sb.append(c);
+ } else
+ break;
+ }
+ int num = Integer.parseInt(sb.toString());
+ int chop = 1 + sb.length();
+ int deltaType = 0;
+ if (date.length() == chop)
+ deltaType = Calendar.DAY_OF_YEAR;
+ else {
+ String deltaTypeString =
date.substring(chop).toLowerCase();
+ if (deltaTypeString.equals("y")
+ || deltaTypeString.equals("year"))
+ deltaType = Calendar.YEAR;
+ else if (
+ deltaTypeString.equals("month")
+ || deltaTypeString.equals("mo"))
+ deltaType = Calendar.MONTH;
+ else if (
+ deltaTypeString.equals("week")
+ || deltaTypeString.equals("w"))
+ deltaType = Calendar.WEEK_OF_YEAR;
+ else if (
+ deltaTypeString.equals("day")
+ || deltaTypeString.equals("d"))
+ deltaType = Calendar.DAY_OF_YEAR;
+ else if (
+ deltaTypeString.equals("hour")
+ || deltaTypeString.equals("h"))
+ deltaType = Calendar.HOUR;
+ else if (
+ deltaTypeString.equals("minute")
+ ||
deltaTypeString.equals("min"))
+ deltaType = Calendar.MINUTE;
+ else if (
+ deltaTypeString.equals("second")
+ || deltaTypeString.equals("s")
+ ||
deltaTypeString.equals("sec"))
+ deltaType = Calendar.SECOND;
+ else
+ throw new NumberFormatException(
+ "unknown time/date delta type:
" + deltaTypeString);
+ GregorianCalendar gc = new GregorianCalendar();
+ gc.add(deltaType, (date.charAt(0) == '+') ? num
: -num);
+ return gc.getTime().getTime();
+ }
+ }
+
+ int dash = date.indexOf('-');
+
+ if (!((dash == -1) && (date.length() == 8))
+ && !((dash == 8) && (date.length() == 17)))
+ throw new NumberFormatException(
+ "Date time: " + date + " not correct.");
+ int year = Integer.parseInt(date.substring(0, 4));
+ int month = Integer.parseInt(date.substring(4, 6));
+ int day = Integer.parseInt(date.substring(6, 8));
+
+ int hour = dash == -1 ? 0 : Integer.parseInt(date.substring(9,
11));
+ int minute = dash == -1 ? 0 :
Integer.parseInt(date.substring(12, 14));
+ int second = dash == -1 ? 0 :
Integer.parseInt(date.substring(15, 17));
+
+ // Note that month is zero based in GregorianCalender!
+ try {
+ return (
+ new GregorianCalendar(
+ year,
+ month - 1,
+ day,
+ hour,
+ minute,
+ second))
+ .getTime()
+ .getTime();
+ } catch (Exception e) {
+ e.printStackTrace();
+ // The API docs don't say which exception is thrown on
bad numbers!
+ throw new NumberFormatException("Invalid date " + date
+ ": " + e);
+ }
+
+ }
+
+ public static final String secToDateTime(long time) {
+ //Calendar c = Calendar.getInstance();
+ //c.setTime(new Date(time));
+ //gc.setTimeInMillis(time*1000);
+
+ DateFormat f = new SimpleDateFormat("yyyyMMdd-HH:mm:ss");
+ //String dateString = f.format(c.getTime());
+ String dateString = f.format(new Date(time * 1000));
+
+ if (dateString.endsWith("-00:00:00"))
+ dateString = dateString.substring(0, 8);
+
+ return dateString;
+ }
+
+ public static final int compareBytes(byte[] b1, byte[] b2) {
+ int len = Math.max(b1.length, b2.length);
+ for (int i = 0; i < len; ++i) {
+ if (i == b1.length)
+ return i == b2.length ? 0 : -1;
+ else if (i == b2.length)
+ return 1;
+ else if ((0xff & b1[i]) > (0xff & b2[i]))
+ return 1;
+ else if ((0xff & b1[i]) < (0xff & b2[i]))
+ return -1;
+ }
+ return 0;
+ }
+
+ public static final int compareBytes(
+ byte[] a,
+ byte[] b,
+ int aoff,
+ int boff,
+ int len) {
+ for (int i = 0; i < len; ++i) {
+ if (i + aoff == a.length)
+ return i + boff == b.length ? 0 : -1;
+ else if (i + boff == b.length)
+ return 1;
+ else if ((0xff & a[i + aoff]) > (0xff & b[i + boff]))
+ return 1;
+ else if ((0xff & a[i + aoff]) < (0xff & b[i + boff]))
+ return -1;
+ }
+ return 0;
+ }
+
+ public static final boolean byteArrayEqual(byte[] a, byte[] b) {
+ if (a.length != b.length)
+ return false;
+ for (int i = 0; i < a.length; ++i)
+ if (a[i] != b[i])
+ return false;
+ return true;
+ }
+
+ public static final boolean byteArrayEqual(
+ byte[] a,
+ byte[] b,
+ int aoff,
+ int boff,
+ int len) {
+ if ((a.length < aoff + len) || (b.length < boff + len))
+ return false;
+ for (int i = 0; i < len; ++i)
+ if (a[i + aoff] != b[i + boff])
+ return false;
+ return true;
+ }
+
+ /**
+ * Compares byte arrays lexicographically.
+ */
+ public static final class ByteArrayComparator implements Comparator {
+ public final int compare(Object o1, Object o2) {
+ return compare((byte[]) o1, (byte[]) o2);
+ }
+ public static final int compare(byte[] o1, byte[] o2) {
+ return compareBytes(o1, o2);
+ }
+ }
+
+ // could add stuff like IntegerComparator, LongComparator etc.
+ // if we need it
+
+ public static final int hashCode(byte[] b) {
+ return hashCode(b, 0, b.length);
+ }
+
+ /**
+ * A generic hashcode suited for byte arrays that are more or less
random.
+ */
+ public static final int hashCode(byte[] b, int ptr, int length) {
+ int h = 0;
+ for (int i = length - 1; i >= 0; --i) {
+ int x = b[ptr+i] & 0xff;
+ h ^= x << ((i & 3) << 3);
+ }
+ return h;
+ }
+
+ /**
+ * Long version of above Not believed to be secure in any sense of the
word :)
+ */
+ public static final long longHashCode(byte[] b) {
+ long h = 0;
+ for (int i = b.length - 1; i >= 0; --i) {
+ int x = b[i] & 0xff;
+ h ^= ((long) x) << ((i & 7) << 3);
+ }
+ return h;
+ }
+
+ /**
+ * @param addr
+ * @return
+ */
+ public static String commaList(Object[] addr) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < addr.length; i++) {
+ sb.append(addr[i]);
+ if (i != addr.length - 1)
+ sb.append(',');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convert an array of longs to an array of bytes, using a
+ * consistent endianness.
+ */
+ public static byte[] longsToBytes(long[] longs) {
+ byte[] buf = new byte[longs.length * 8];
+ for(int i=0;i<longs.length;i++) {
+ long x = longs[i];
+ for(int j=0;j<8;j++) {
+ buf[i*8+j] = (byte)x;
+ x >>= 8;
+ }
+ }
+ return buf;
+ }
+
+ /**
+ * Convert an array of bytes to an array of longs.
+ * @param buf
+ * @return
+ */
+ public static long[] bytesToLongs(byte[] buf) {
+ if(buf.length % 8 != 0) throw new IllegalArgumentException();
+ long[] longs = new long[buf.length/8];
+ for(int i=0;i<longs.length;i++) {
+ long x = 0;
+ for(int j=7;j>=0;j--) {
+ long y = (buf[i*8+j] & 0xff);
+ x = (x << 8) + y;
+ }
+ longs[i] = x;
+ }
+ return longs;
+ }
+
+ /**
+ * Convert an array of bytes to a single long.
+ */
+ public static long bytesToLong(byte[] buf) {
+ if(buf.length < 8) throw new IllegalArgumentException();
+ long x = 0;
+ for(int j=7;j>=0;j--) {
+ long y = (buf[j] & 0xff);
+ x = (x << 8) + y;
+ }
+ return x;
+ }
+
+ /**
+ * Convert an array of bytes to a single int.
+ */
+ public static int bytesToInt(byte[] buf, int offset) {
+ if(buf.length < 4) throw new IllegalArgumentException();
+ int x = 0;
+ for(int j=3;j>=0;j--) {
+ int y = (buf[j+offset] & 0xff);
+ x = (x << 8) + y;
+ }
+ return x;
+ }
+
+ public static byte[] longToBytes(long x) {
+ byte[] buf = new byte[8];
+ for(int j=0;j<8;j++) {
+ buf[j] = (byte)x;
+ x >>= 8;
+ }
+ return buf;
+ }
+
+ public static byte[] intsToBytes(int[] ints) {
+ byte[] buf = new byte[ints.length * 8];
+ for(int i=0;i<ints.length;i++) {
+ long x = ints[i];
+ for(int j=0;j<4;j++) {
+ buf[i*4+j] = (byte)x;
+ x >>= 8;
+ }
+ }
+ return buf;
+ }
+
+ /**
+ * Parse a human-readable string possibly including SI units into a short.
+ * @throws NumberFormatException
+ * if the string is not parseable
+ */
+ public static short parseShort(String s) throws NumberFormatException {
+ short res = 1;
+ int x = s.length() - 1;
+ int idx;
+ try {
+ long[] l =
+ {
+ 1000,
+ 1 << 10 };
+ while ((x >= 0)
+ && ((idx = "kK".indexOf(s.charAt(x))) != -1)) {
+ x--;
+ res *= l[idx];
+ }
+ res *= Double.parseDouble(s.substring(0, x + 1));
+ } catch (ArithmeticException e) {
+ res = Short.MAX_VALUE;
+ throw new NumberFormatException(e.getMessage());
+ }
+ return res;
+ }
+
+ /**
+ * Parse a human-readable string possibly including SI units into an
integer.
+ * @throws NumberFormatException
+ * if the string is not parseable
+ */
+ public static int parseInt(String s) throws NumberFormatException {
+ int res = 1;
+ int x = s.length() - 1;
+ int idx;
+ try {
+ long[] l =
+ {
+ 1000,
+ 1 << 10,
+ 1000 * 1000,
+ 1 << 20,
+ 1000 * 1000 * 1000,
+ 1 << 30 };
+ while ((x >= 0)
+ && ((idx = "kKmMgG".indexOf(s.charAt(x))) !=
-1)) {
+ x--;
+ res *= l[idx];
+ }
+ res *= Double.parseDouble(s.substring(0, x + 1));
+ } catch (ArithmeticException e) {
+ res = Integer.MAX_VALUE;
+ throw new NumberFormatException(e.getMessage());
+ }
+ return res;
+ }
+
+ /**
+ * Parse a human-readable string possibly including SI units into a
long.
+ * @throws NumberFormatException
+ * if the string is not parseable
+ */
+ public static long parseLong(String s) throws NumberFormatException {
+ long res = 1;
+ int x = s.length() - 1;
+ int idx;
+ try {
+ long[] l =
+ {
+ 1000,
+ 1 << 10,
+ 1000 * 1000,
+ 1 << 20,
+ 1000 * 1000 * 1000,
+ 1 << 30,
+ 1000 * 1000 * 1000 * 1000,
+ 1 << 40,
+ 1000 * 1000 * 1000 * 1000 * 1000,
+ 1 << 50,
+ 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
+ 1 << 60 };
+ while ((x >= 0)
+ && ((idx = "kKmMgGtTpPeE".indexOf(s.charAt(x)))
!= -1)) {
+ x--;
+ res *= l[idx];
+ }
+ res *= Double.parseDouble(s.substring(0, x + 1));
+ } catch (ArithmeticException e) {
+ res = Long.MAX_VALUE;
+ throw new NumberFormatException(e.getMessage());
+ }
+ return res;
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/LineReader.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/LineReader.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/LineReader.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,12 @@
+package freenet.support;
+
+import java.io.IOException;
+
+public interface LineReader {
+
+ /**
+ * Read a \n or \r\n terminated line of UTF-8 or ISO-8859-1.
+ */
+ public String readLine(int maxLength, int bufferSize, boolean utf)
throws IOException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/SimpleFieldSet.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/SimpleFieldSet.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/SimpleFieldSet.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,479 @@
+package freenet.support;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import freenet.support.io.LineReader;
+
+/**
+ * @author amphibian
+ *
+ * Very very simple FieldSet type thing, which uses the standard
+ * Java facilities.
+ */
+public class SimpleFieldSet {
+
+ private final Map values;
+ private Map subsets;
+ private String endMarker;
+ static public final char MULTI_LEVEL_CHAR = '.';
+
+ public SimpleFieldSet(BufferedReader br) throws IOException {
+ values = new HashMap();
+ subsets = null;
+ read(br);
+ }
+
+ public SimpleFieldSet(LineReader lis, int maxLineLength, int
lineBufferSize, boolean tolerant, boolean utf8OrIso88591) throws IOException {
+ values = new HashMap();
+ subsets = null;
+ read(lis, maxLineLength, lineBufferSize, tolerant, utf8OrIso88591);
+ }
+
+ /**
+ * Empty constructor
+ */
+ public SimpleFieldSet() {
+ values = new HashMap();
+ subsets = null;
+ }
+
+ /**
+ * Construct from a string.
+ * @throws IOException if the string is too short or invalid.
+ */
+ public SimpleFieldSet(String content, boolean multiLevel) throws
IOException {
+ values = new HashMap();
+ subsets = null;
+ StringReader sr = new StringReader(content);
+ BufferedReader br = new BufferedReader(sr);
+ read(br);
+ }
+
+ /**
+ * Read from disk
+ * Format:
+ * blah=blah
+ * blah=blah
+ * End
+ */
+ private void read(BufferedReader br) throws IOException {
+ boolean firstLine = true;
+ while(true) {
+ String line = br.readLine();
+ if(line == null) {
+ if(firstLine) throw new EOFException();
+ throw new IOException();
+ }
+ firstLine = false;
+ int index = line.indexOf('=');
+ if(index >= 0) {
+ // Mapping
+ String before = line.substring(0, index);
+ String after = line.substring(index+1);
+ put(before, after);
+ } else {
+ endMarker = line;
+ return;
+ }
+
+ }
+ }
+
+ /**
+ * Read from disk
+ * Format:
+ * blah=blah
+ * blah=blah
+ * End
+ * @param utfOrIso88591 If true, read as UTF-8, otherwise read as
ISO-8859-1.
+ */
+ private void read(LineReader br, int maxLength, int bufferSize, boolean
tolerant, boolean utfOrIso88591) throws IOException {
+ boolean firstLine = true;
+ while(true) {
+ String line = br.readLine(maxLength, bufferSize, utfOrIso88591);
+ if(line == null) {
+ if(firstLine) throw new EOFException();
+ if(!tolerant)
+ throw new IOException("No end marker");
+ return;
+ }
+ if((line.length() == 0) && tolerant) continue; // ignore
+ firstLine = false;
+ int index = line.indexOf('=');
+ if(index >= 0) {
+ // Mapping
+ String before = line.substring(0, index);
+ String after = line.substring(index+1);
+ put(before, after);
+ } else {
+ endMarker = line;
+ return;
+ }
+
+ }
+ }
+
+ public synchronized String get(String key) {
+ int idx = key.indexOf(MULTI_LEVEL_CHAR);
+ if(idx == -1)
+ return (String) values.get(key);
+ else if(idx == 0)
+ return null;
+ else {
+ if(subsets == null) return null;
+ String before = key.substring(0, idx);
+ String after = key.substring(idx+1);
+ SimpleFieldSet fs = (SimpleFieldSet)
(subsets.get(before));
+ if(fs == null) return null;
+ return fs.get(after);
+ }
+ }
+
+ public String[] getAll(String key) {
+ return split(get(key));
+ }
+
+ private static final String[] split(String string) {
+ if(string == null) return new String[0];
+ return string.split(";"); // slower???
+// int index = string.indexOf(';');
+// if(index == -1) return null;
+// Vector v=new Vector();
+// v.removeAllElements();
+// while(index>0){
+// // Mapping
+// String before = string.substring(0, index);
+// String after = string.substring(index+1);
+// v.addElement(before);
+// string=after;
+// index = string.indexOf(';');
+// }
+//
+// return (String[]) v.toArray();
+ }
+
+ public synchronized void put(String key, String value) {
+ int idx;
+ if(value == null) return;
+ if((idx = key.indexOf(MULTI_LEVEL_CHAR)) == -1) {
+ String x = (String) values.get(key);
+
+ if(x == null) {
+ values.put(key, value);
+ } else {
+ values.put(key,
((String)values.get(key))+";"+value);
+ }
+ } else {
+ String before = key.substring(0, idx);
+ String after = key.substring(idx+1);
+ SimpleFieldSet fs = null;
+ if(subsets == null)
+ subsets = new HashMap();
+ fs = (SimpleFieldSet) (subsets.get(before));
+ if(fs == null) {
+ fs = new SimpleFieldSet();
+ subsets.put(before, fs);
+ }
+ fs.put(after, value);
+ }
+ }
+
+ public void put(String key, int value) {
+ put(key, Integer.toString(value));
+ }
+
+ public void put(String key, long value) {
+ put(key, Long.toString(value));
+ }
+
+ public void put(String key, short value) {
+ put(key, Short.toString(value));
+ }
+
+ public void put(String key, char c) {
+ put(key, ""+c);
+ }
+
+ public void put(String key, boolean b) {
+ put(key, Boolean.toString(b));
+ }
+
+ public void put(String key, double windowSize) {
+ put(key, Double.toString(windowSize));
+ }
+
+ /**
+ * Write the contents of the SimpleFieldSet to a Writer.
+ * @param osr
+ */
+ public void writeTo(Writer w) throws IOException {
+ writeTo(w, "", false);
+ }
+
+ synchronized void writeTo(Writer w, String prefix, boolean noEndMarker)
throws IOException {
+ for(Iterator i = values.entrySet().iterator();i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ w.write(prefix+key+"="+value+"\n");
+ }
+ if(subsets != null) {
+ for(Iterator i = subsets.entrySet().iterator();i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ SimpleFieldSet subset = (SimpleFieldSet)
entry.getValue();
+ if(subset == null) throw new NullPointerException();
+ subset.writeTo(w, prefix+key+MULTI_LEVEL_CHAR, true);
+ }
+ }
+ if(!noEndMarker) {
+ if(endMarker == null)
+ w.write("End\n");
+ else
+ w.write(endMarker+"\n");
+ }
+ }
+
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ try {
+ writeTo(sw);
+ } catch (IOException e) {
+ System.err.println("WTF?!: "+e+" in toString()!"+e);
+ }
+ return sw.toString();
+ }
+
+ public String getEndMarker() {
+ return endMarker;
+ }
+
+ public void setEndMarker(String s) {
+ endMarker = s;
+ }
+
+ public synchronized SimpleFieldSet subset(String key) {
+ if(subsets == null) return null;
+ int idx = key.indexOf(MULTI_LEVEL_CHAR);
+ if(idx == -1)
+ return (SimpleFieldSet) subsets.get(key);
+ String before = key.substring(0, idx);
+ String after = key.substring(idx+1);
+ SimpleFieldSet fs = (SimpleFieldSet) subsets.get(before);
+ if(fs == null) return null;
+ return fs.subset(after);
+ }
+
+ public Iterator keyIterator() {
+ return new KeyIterator("");
+ }
+
+ KeyIterator keyIterator(String prefix) {
+ return new KeyIterator(prefix);
+ }
+
+ public class KeyIterator implements Iterator {
+
+ final Iterator valuesIterator;
+ final Iterator subsetIterator;
+ KeyIterator subIterator;
+ String prefix;
+
+ public KeyIterator(String prefix) {
+ valuesIterator = values.keySet().iterator();
+ if(subsets != null)
+ subsetIterator = subsets.keySet().iterator();
+ else
+ subsetIterator = null;
+ this.prefix = prefix;
+ }
+
+ public boolean hasNext() {
+ synchronized(SimpleFieldSet.this) {
+ if(valuesIterator.hasNext()) return true;
+ if((subIterator != null) &&
subIterator.hasNext()) return true;
+ if(subIterator != null) subIterator = null;
+ return false;
+ }
+ }
+
+ public final Object next() {
+ return nextKey();
+ }
+
+ public String nextKey() {
+ synchronized(SimpleFieldSet.this) {
+ String ret = null;
+ if(ret == null && valuesIterator.hasNext()) {
+ return prefix + valuesIterator.next();
+ }
+ while(true) {
+ // Iterate subsets.
+ if(subIterator != null &&
subIterator.hasNext()) {
+ if(ret != null)
+ // Found next but one,
can return next
+ return ret;
+ ret = (String)
subIterator.next();
+ if(subIterator.hasNext()) {
+ if(ret != null) return
ret;
+ } else {
+ subIterator = null;
+ }
+ } else
+ subIterator = null;
+ if(subsetIterator != null &&
subsetIterator.hasNext()) {
+ String key = (String)
subsetIterator.next();
+ SimpleFieldSet fs =
(SimpleFieldSet) subsets.get(key);
+ String newPrefix = prefix + key
+ MULTI_LEVEL_CHAR;
+ subIterator =
fs.keyIterator(newPrefix);
+ }
+ }
+ }
+ }
+
+ public synchronized void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void put(String key, SimpleFieldSet fs) {
+ if(fs == null) return; // legal no-op, because used everywhere
+ if(fs.isEmpty())
+ throw new IllegalArgumentException("Empty");
+ if(subsets == null)
+ subsets = new HashMap();
+ if(subsets.containsKey(key))
+ throw new IllegalArgumentException("Already contains
"+key+" but trying to add a SimpleFieldSet!");
+ subsets.put(key, fs);
+ }
+
+ public synchronized void removeValue(String key) {
+ int idx;
+ if((idx = key.indexOf(MULTI_LEVEL_CHAR)) == -1) {
+ values.remove(key);
+ } else {
+ if(subsets == null) return;
+ String before = key.substring(0, idx);
+ String after = key.substring(idx+1);
+ SimpleFieldSet fs = (SimpleFieldSet)
(subsets.get(before));
+ if(fs == null) {
+ return;
+ }
+ fs.removeValue(after);
+ if(fs.isEmpty()) {
+ subsets.remove(before);
+ if(subsets.isEmpty())
+ subsets = null;
+ }
+ }
+ }
+
+ public synchronized void removeSubset(String key) {
+ if(subsets == null) return;
+ int idx;
+ if((idx = key.indexOf(MULTI_LEVEL_CHAR)) == -1) {
+ subsets.remove(key);
+ } else {
+ String before = key.substring(0, idx);
+ String after = key.substring(idx+1);
+ SimpleFieldSet fs = (SimpleFieldSet)
(subsets.get(before));
+ if(fs == null) {
+ return;
+ }
+ fs.removeSubset(after);
+ if(fs.isEmpty()) {
+ subsets.remove(before);
+ if(subsets.isEmpty())
+ subsets = null;
+ }
+ }
+ }
+
+ /** Is this SimpleFieldSet empty? */
+ public boolean isEmpty() {
+ return values.isEmpty() && (subsets == null ||
subsets.isEmpty());
+ }
+
+ public Iterator directSubsetNameIterator() {
+ return subsets.keySet().iterator();
+ }
+
+ public String[] namesOfDirectSubsets() {
+ return (String[]) subsets.keySet().toArray(new
String[subsets.size()]);
+ }
+
+ public static SimpleFieldSet readFrom(File f) throws IOException {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(f);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ InputStreamReader isr;
+ try {
+ isr = new InputStreamReader(bis, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ System.err.println("Impossible: "+e);
+ fis.close();
+ return null;
+ }
+ BufferedReader br = new BufferedReader(isr);
+ SimpleFieldSet fs = new SimpleFieldSet(br);
+ br.close();
+ fis = null;
+ return fs;
+ } finally {
+ try {
+ if(fis != null) fis.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+ public long getInt(String key, int def) {
+ String s = get(key);
+ if(s == null) return def;
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ public double getDouble(String key, double def) {
+ String s = get(key);
+ if(s == null) return def;
+ try {
+ return Double.parseDouble(s);
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ public long getLong(String key, long def) {
+ String s = get(key);
+ if(s == null) return def;
+ try {
+ return Long.parseLong(s);
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ public boolean getBoolean(String key, boolean def) {
+ return Fields.stringToBool(get(key), def);
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/URLDecoder.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/URLDecoder.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/URLDecoder.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,104 @@
+package freenet.support;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/*
+ This code is part of the Java Adaptive Network Client by Ian Clarke.
+ It is distributed under the GNU Public Licence (GPL) version 2. See
+ http://www.gnu.org/ for further details of the GPL.
+*/
+
+
+/**
+ * The class contains a utility method for converting a
+ * <code>String</code> out of a MIME format called
+ * "<code>x-www-form-urlencoded</code>" format.
+ * <p>
+ * To convert a <code>String</code>, each character is examined in turn:
+ * <ul>
+ * <li>The ASCII characters '<code>a</code>' through '<code>z</code>',
+ * '<code>A</code>' through '<code>Z</code>', and '<code>0</code>'
+ * through '<code>9</code>' remain the same.
+ * <li>The plus sign '<code>+</code>' is converted into a
+ * space character '<code> </code>'.
+ * <li>The percent sign '<code>%</code>' must be followed by a
+ * two-digit hexadecimal number, and is converted into the
+ * corresponding 8-bit character.
+ * <li>The following "safe" characters [RFC 1738] are passed as is,
+ * if they appear:
+ * <code>$ - _ . + ! * ' ( ) ,</code>
+ * <li>Anything else encountered, though strictly speaking illegal,
+ * is passed as is.
+ * </ul>
+ *
+ * @author <a href="http://www.doc.ic.ac.uk/~twh1/">Theodore Hong</a>
+ **/
+
+public class URLDecoder
+{
+ // test harness
+ public static void main(String[] args) throws URLEncodedFormatException {
+ for (int i = 0; i < args.length; i++) {
+ System.out.println(args[i] + " -> " + decode(args[i]));
+ }
+ }
+
+ /**
+ * Characters which will be passed unaltered.
+ **/
+ private static final String safeCharList = "$-_.+!*'(),";
+
+ /**
+ * Translates a string out of x-www-form-urlencoded format.
+ *
+ * @param s String to be translated.
+ * @return the translated String.
+ *
+ **/
+ public static String decode(String s) throws URLEncodedFormatException {
+ if (s.length() == 0)
+ return "";
+ int len = s.length();
+ ByteArrayOutputStream decodedBytes = new
ByteArrayOutputStream();
+
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (Character.isLetterOrDigit(c))
+ decodedBytes.write(c);
+ else if (c == '+')
+ decodedBytes.write(' ');
+ else if (safeCharList.indexOf(c) != -1)
+ decodedBytes.write(c);
+ else if (c == '%') {
+ if (i >= len - 2) {
+ throw new URLEncodedFormatException(s);
+ }
+ char[] hexChars = new char[2];
+
+ hexChars[0] = s.charAt(++i);
+ hexChars[1] = s.charAt(++i);
+
+ String hexval = new String(hexChars);
+ try {
+ long read = Fields.hexToLong(hexval);
+ if (read == 0)
+ throw new
URLEncodedFormatException("Can't encode" + " 00");
+ decodedBytes.write((int) read);
+ } catch (NumberFormatException nfe) {
+ throw new URLEncodedFormatException(s);
+ }
+ } else
+ decodedBytes.write(c);
+ // throw new URLEncodedFormatException(s);
+ }
+ try {
+ decodedBytes.close();
+ return new String(decodedBytes.toByteArray(), "utf-8");
+ } catch (IOException ioe1) {
+ /* if this throws something's wrong */
+ }
+ throw new URLEncodedFormatException(s);
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/URLEncodedFormatException.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/URLEncodedFormatException.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/URLEncodedFormatException.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,20 @@
+package freenet.support;
+
+/*
+ This code is part of the Java Adaptive Network Client by Ian Clarke.
+ It is distributed under the GNU Public Licence (GPL) version 2. See
+ http://www.gnu.org/ for further details of the GPL.
+*/
+
+
+/**
+ * Thrown when trying to decode a string which is not in
+ * "<code>x-www-form-urlencoded</code>" format.
+ **/
+
+public class URLEncodedFormatException extends Exception {
+ private static final long serialVersionUID = -1;
+
+ URLEncodedFormatException () {}
+ URLEncodedFormatException (String s) { super(s); }
+}
Added: trunk/apps/blueBunny/src/freenet/support/URLEncoder.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/URLEncoder.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/URLEncoder.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,39 @@
+package freenet.support;
+
+public class URLEncoder {
+ // Moved here from FProxy by amphibian
+ final static String safeURLCharacters =
"@*-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+
+ /**
+ * Encode a string for inclusion in HTML tags
+ *
+ * @param URL String to encode
+ * @return HTML-safe version of string
+ */
+ public final static String encode(String URL) {
+ StringBuffer enc = new StringBuffer(URL.length());
+ for (int i = 0; i < URL.length(); ++i) {
+ char c = URL.charAt(i);
+ if (safeURLCharacters.indexOf(c) >= 0) {
+ enc.append(c);
+ } else {
+ // Too harsh.
+ // if (c < 0 || c > 255)
+ // throw new RuntimeException("illegal code "+c+" of char
'"+URL.charAt(i)+"'");
+ // else
+
+ // Just keep lsb like:
+ // http://java.sun.com/j2se/1.3/docs/api/java/net/URLEncoder.html
+ c = (char) (c & '\u00ff');
+ if (c < 16) {
+ enc.append("%0");
+ } else {
+ enc.append("%");
+ }
+ enc.append(Integer.toHexString(c));
+ }
+ }
+ return enc.toString();
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/io/LineReader.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/io/LineReader.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/io/LineReader.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -0,0 +1,12 @@
+package freenet.support.io;
+
+import java.io.IOException;
+
+public interface LineReader {
+
+ /**
+ * Read a \n or \r\n terminated line of UTF-8 or ISO-8859-1.
+ */
+ public String readLine(int maxLength, int bufferSize, boolean utf)
throws IOException;
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/io/LineReadingInputStream.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/io/LineReadingInputStream.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/io/LineReadingInputStream.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,49 @@
+package freenet.support.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A FilterInputStream which provides readLine().
+ */
+public class LineReadingInputStream extends FilterInputStream implements
LineReader {
+
+ public LineReadingInputStream(InputStream in) {
+ super(in);
+ }
+
+ private byte[] buf;
+
+ /**
+ * Read a \n or \r\n terminated line of UTF-8 or ISO-8859-1.
+ */
+ public String readLine(int maxLength, int bufferSize, boolean utf)
throws IOException {
+ if(maxLength < bufferSize)
+ bufferSize = maxLength;
+ if(buf == null)
+ buf = new byte[Math.max(Math.min(128,maxLength),
Math.min(1024, bufferSize))];
+ int ctr = 0;
+ while(true) {
+ int x = read();
+ if(x == -1) {
+ if(ctr == 0) return null;
+ return new String(buf, 0, ctr, utf ? "UTF-8" :
"ISO-8859-1");
+ }
+ // REDFLAG this is definitely safe with the above
charsets, it may not be safe with some wierd ones.
+ if(x == '\n') {
+ if(ctr == 0) return "";
+ if(buf[ctr-1] == '\r') ctr--;
+ return new String(buf, 0, ctr, utf ? "UTF-8" :
"ISO-8859-1");
+ }
+ if(ctr >= buf.length) {
+ if(buf.length == maxLength) throw new
TooLongException();
+ byte[] newBuf = new byte[Math.min(buf.length *
2, maxLength)];
+ System.arraycopy(buf, 0, newBuf, 0, buf.length);
+ buf = newBuf;
+ }
+ buf[ctr++] = (byte)x;
+ }
+ }
+
+}
Added: trunk/apps/blueBunny/src/freenet/support/io/TooLongException.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/support/io/TooLongException.java
2006-08-13 22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/support/io/TooLongException.java
2006-08-13 22:14:40 UTC (rev 10071)
@@ -0,0 +1,9 @@
+package freenet.support.io;
+
+import java.io.IOException;
+
+/** Exception thrown by a LineReadingInputStream when a line is too long. */
+public class TooLongException extends IOException {
+ private static final long serialVersionUID = -1;
+
+}
\ No newline at end of file
Modified: trunk/apps/blueBunny/src/freenet/systray/Systray.java
===================================================================
--- trunk/apps/blueBunny/src/freenet/systray/Systray.java 2006-08-13
22:01:32 UTC (rev 10070)
+++ trunk/apps/blueBunny/src/freenet/systray/Systray.java 2006-08-13
22:14:40 UTC (rev 10071)
@@ -11,11 +11,30 @@
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import java.io.File;
+import java.io.IOException;
+import freenet.config.FilePersistentConfig;
+
public class Systray {
private boolean isNodeAlive;
+ private static FilePersistentConfig cfg;
public static void main(String[] args) {
+ // Load the config
+ File configFilename = new File("systray.ini");
+ try{
+ cfg = new FilePersistentConfig(configFilename);
+ }catch(IOException e){
+ System.out.println("Error : "+e);
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ cfg.finishedInit();
+ cfg.store();
+
+ //SubConfig loggingConfig = new SubConfig("node", cfg);
+
Systray s = new Systray();
}
@@ -25,13 +44,13 @@
if (SystemTray.isSupported()) {
final TrayIcon trayIcon;
SystemTray tray = SystemTray.getSystemTray();
- Image image =
Toolkit.getDefaultToolkit().getImage("logo.jpg");
+ Image image =
Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/freenet/systray/resources/logo.jpg"));
MouseListener mouseListener = new MouseListener() {
public void mouseClicked(MouseEvent e) {
System.out.println("Tray Icon - Mouse
clicked!");
- if(isNodeAlive && (e.getButton() ==
MouseEvent.BUTTON1))
+ if(isNodeAlive && (e.getButton() == 1))
BareBonesBrowserLaunch.launch("http://127.0.0.1:8888/");
}
@@ -88,11 +107,14 @@
openConfigItem.addActionListener(openConfigListener);
openConfigItem.setEnabled(false);
popup.add(exitItem);
+ popup.addSeparator();
popup.add(openFproxyItem);
popup.add(openWebsiteItem);
+ popup.addSeparator();
popup.add(openConfigItem);
+ popup.setLabel("Freenet 0.7");
- trayIcon = new TrayIcon(image, "Tray Demo", popup);
+ trayIcon = new TrayIcon(image, "Freenet 0.7", popup);
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {