Author: dieppe
Date: 2008-04-22 23:39:58 +0000 (Tue, 22 Apr 2008)
New Revision: 19512

Added:
   trunk/apps/thingamablog/src/net/sf/thingamablog/util/io/FileUtil.java
   trunk/apps/thingamablog/src/thingamablog/
   trunk/apps/thingamablog/src/thingamablog/l10n/
   trunk/apps/thingamablog/src/thingamablog/l10n/FSParseException.java
   trunk/apps/thingamablog/src/thingamablog/l10n/Fields.java
   trunk/apps/thingamablog/src/thingamablog/l10n/L10n.java
   trunk/apps/thingamablog/src/thingamablog/l10n/LineReader.java
   trunk/apps/thingamablog/src/thingamablog/l10n/SimpleFieldSet.java
   trunk/apps/thingamablog/src/thingamablog/l10n/i18n.java
Modified:
   trunk/apps/thingamablog/src/net/sf/thingamablog/App.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/TBGlobals.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/blog/TBWeblog.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/GUILoginPrompt.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/ImageViewerDialog.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/JAboutBox.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/StandardDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ExportTemplatePackDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedPropertiesDialog.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedTableModel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ImportEntriesDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/InstallTemplateDialog.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/LogPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/SelectTemplatePanel.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/StatusBar.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBOptionsDialog.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBSearchDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBViewerPaneModel.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TaskDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplatePropertiesPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplateSelectionPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ThingamablogFrame.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTableModel.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTreeModel.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/EntryEditor.java
   trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/HTMLEditor.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/ASCIIPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/EditableList.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBArchivingPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCategoriesPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCustomVariablesPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBEmailPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogNodeWizardDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogWizardDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFrontPagePanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBPublishTransportPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBTemplatesPanel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWeblogPropertiesDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWizardDialog.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogEditableListModel.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogPropertiesDialogFactory.java
   
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/XmlRpcPingPanel.java
Log:
Add all the sources needed for the L10n framework.
Modify all (except 3) files containing i18n messages.

Modified: trunk/apps/thingamablog/src/net/sf/thingamablog/App.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/App.java    2008-04-22 
22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/App.java    2008-04-22 
23:39:58 UTC (rev 19512)
@@ -33,13 +33,13 @@
 import javax.swing.SwingUtilities;
 import javax.swing.UIManager;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.gui.app.ExperiencedBlue;
 import net.sf.thingamablog.gui.app.ThingamablogFrame;

 import com.jgoodies.plaf.LookUtils;
 import com.jgoodies.plaf.Options;
 import com.jgoodies.plaf.plastic.PlasticLookAndFeel;
+import thingamablog.l10n.i18n;

 /** Application starter */

@@ -86,18 +86,8 @@

         //set up the I18n resource bundles
         if(TBGlobals.getProperty("LANG_LOCALE") != null)
-               I18n.setLocale(TBGlobals.getProperty("LANG_LOCALE"));
+               i18n.setLocale(TBGlobals.getProperty("LANG_LOCALE"));

-        I18n.setBundleForPackage("net.sf.thingamablog.blog", langPack);
-        I18n.setBundleForPackage("net.sf.thingamablog.gui", langPack);
-        I18n.setBundleForPackage("net.sf.thingamablog.gui.app", langPack);
-        I18n.setBundleForPackage("net.sf.thingamablog.gui.editor", langPack);
-        I18n.setBundleForPackage("net.sf.thingamablog.gui.properties", 
langPack);
-        I18n.setBundleForPackage("net.atlanticbb.tantlinger.ui", langPack);
-        I18n.setBundleForPackage("net.atlanticbb.tantlinger.ui.text", 
langPack);
-        I18n.setBundleForPackage("net.atlanticbb.tantlinger.ui.text.actions", 
langPack);
-        I18n.setBundleForPackage("net.atlanticbb.tantlinger.ui.text.dialogs", 
langPack);
-                
         //OSX properties
         System.setProperty("apple.laf.useScreenMenuBar", "true");
         System.setProperty("com.apple.mrj.application.apple.menu.about.name", 
TBGlobals.APP_NAME);

Modified: trunk/apps/thingamablog/src/net/sf/thingamablog/TBGlobals.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/TBGlobals.java      
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/TBGlobals.java      
2008-04-22 23:39:58 UTC (rev 19512)
@@ -51,13 +51,13 @@
 public class TBGlobals
 {
        /** The name of the application */
-       public static final String APP_NAME = "@APP_NAME@";
+       public static final String APP_NAME = "Thingamablog";
        /** The version of the application */
-       public static final String VERSION = "@VERSION@";
+       public static final String VERSION = "1.1b6";
        /** The build of the application */
-       public static final String BUILD = "@BUILD@";
+       public static final String BUILD = "20080423.120";
        /** The home page of the application */
-       public static final String APP_URL = "@APP_URL@";
+       public static final String APP_URL = 
"http://thingamablog.sourceforge.net";;

        /** Platform specific path separator */
        public static final String SEP = System.getProperty("file.separator");

Modified: trunk/apps/thingamablog/src/net/sf/thingamablog/blog/TBWeblog.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/blog/TBWeblog.java  
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/blog/TBWeblog.java  
2008-04-22 23:39:58 UTC (rev 19512)
@@ -35,11 +35,10 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;

-
-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.io.IOUtils;
 import net.sf.thingamablog.generator.PageGenerator;
 import net.sf.thingamablog.transport.FCPTransport;
+import thingamablog.l10n.i18n;


 /**
@@ -51,7 +50,6 @@
 public class TBWeblog extends Weblog
 {    
     private static Logger logger = 
Logger.getLogger("net.sf.thingamablog.blog");
-    private static I18n i18n = I18n.getInstance("net.sf.thingamablog.blog");

        //vaid chars for category page files names
        private static final String VALID_CHARS = 

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/GUILoginPrompt.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/GUILoginPrompt.java     
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/GUILoginPrompt.java     
2008-04-22 23:39:58 UTC (rev 19512)
@@ -28,8 +28,8 @@
 import javax.swing.JPasswordField;
 import javax.swing.JTextField;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.transport.LoginPrompt;
+import thingamablog.l10n.i18n;


 /**
@@ -44,8 +44,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui");

     private JPasswordField pwField;
        private JTextField userField;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/ImageViewerDialog.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/ImageViewerDialog.java  
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/ImageViewerDialog.java  
2008-04-22 23:39:58 UTC (rev 19512)
@@ -38,8 +38,8 @@
 import javax.swing.JToolBar;
 import javax.swing.border.BevelBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
+import thingamablog.l10n.i18n;


 /**
@@ -54,8 +54,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui");

     private ImagePanel imagePanel;
        private File currentImageFile;

Modified: trunk/apps/thingamablog/src/net/sf/thingamablog/gui/JAboutBox.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/JAboutBox.java  
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/JAboutBox.java  
2008-04-22 23:39:58 UTC (rev 19512)
@@ -39,7 +39,6 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
-import java.net.URL;
 import java.util.StringTokenizer;

 import javax.swing.Box;
@@ -53,11 +52,8 @@
 import javax.swing.JTextArea;
 import javax.swing.Scrollable;
 import javax.swing.border.EmptyBorder;
+import thingamablog.l10n.i18n;

-import net.atlanticbb.tantlinger.i18n.I18n;
-
-import org.jdesktop.jdic.desktop.Desktop;
-
 //import com.Ostermiller.util.Browser;

 /**
@@ -77,7 +73,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui");
     private final String MAILTO = "mailto:";; //$NON-NLS-1$

        private JLabel appTitleLabel = new JLabel();

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/StandardDialog.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/StandardDialog.java     
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/StandardDialog.java     
2008-04-22 23:39:58 UTC (rev 19512)
@@ -11,12 +11,11 @@
 import javax.swing.JButton;
 import javax.swing.JDialog;
 import javax.swing.JPanel;
+import thingamablog.l10n.i18n;

-import net.atlanticbb.tantlinger.i18n.I18n;



-
 /**
  * This class implements a standard data entry dialog with "Ok" and
  * "Cancel" buttons. Subclasses can override the isDataValid(),
@@ -35,8 +34,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui");

     // Constants
     public static final int BUTTONS_CENTER = FlowLayout.CENTER;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ExportTemplatePackDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ExportTemplatePackDialog.java
       2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ExportTemplatePackDialog.java
       2008-04-22 23:39:58 UTC (rev 19512)
@@ -38,7 +38,6 @@
 import javax.swing.tree.TreeModel;
 import javax.swing.tree.TreePath;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.io.IOUtils;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
@@ -47,6 +46,7 @@
 import net.sf.thingamablog.blog.TemplatePack;
 import net.sf.thingamablog.blog.ZipExportableTemplatePack;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;



@@ -60,7 +60,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app"); //$NON-NLS-1$
     private static final String TITLE = i18n.str("export_templates"); 
//$NON-NLS-1$

     private JLabel msgLabel = null;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedPropertiesDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedPropertiesDialog.java
   2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedPropertiesDialog.java
   2008-04-22 23:39:58 UTC (rev 19512)
@@ -40,10 +40,10 @@
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.TitledBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.feed.Feed;
 import net.sf.thingamablog.gui.LabelledItemPanel;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -55,8 +55,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     private Feed feed;
        private JTextField urlField;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedTableModel.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedTableModel.java 
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/FeedTableModel.java 
2008-04-22 23:39:58 UTC (rev 19512)
@@ -24,9 +24,9 @@
 package net.sf.thingamablog.gui.app;


-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.feed.FeedItem;
 import net.sf.thingamablog.gui.table.DefaultSortTableModel;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -40,8 +40,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     public static final Object READ =  " "; //$NON-NLS-1$
        public static final Object ITEM = i18n.str("item"); //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ImportEntriesDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ImportEntriesDialog.java
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ImportEntriesDialog.java
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -49,11 +49,11 @@
 import javax.swing.event.CaretEvent;
 import javax.swing.event.CaretListener;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.gui.CustomFileFilter;
 import net.sf.thingamablog.xml.RSSImportExport;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -67,8 +67,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     private JButton cancelButton;
        private JButton importButton;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/InstallTemplateDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/InstallTemplateDialog.java
  2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/InstallTemplateDialog.java
  2008-04-22 23:39:58 UTC (rev 19512)
@@ -25,12 +25,12 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.io.IOUtils;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.TemplatePack;
 import net.sf.thingamablog.blog.ZipTemplatePack;
+import thingamablog.l10n.i18n;



@@ -39,9 +39,7 @@
  *
  */
 public class InstallTemplateDialog extends JDialog
-{
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");
-    
+{   
     private static final long serialVersionUID = 1L;
     private JLabel instrLabel = null;
     private JButton openButton = null;

Modified: trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/LogPanel.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/LogPanel.java       
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/LogPanel.java       
2008-04-22 23:39:58 UTC (rev 19512)
@@ -32,13 +32,13 @@
 import javax.swing.text.SimpleAttributeSet;
 import javax.swing.text.StyleConstants;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.blog.PingProgress;
 import net.sf.thingamablog.blog.PingService;
 import net.sf.thingamablog.blog.PublishProgress;
 import net.sf.thingamablog.transport.MailTransportProgress;
+import thingamablog.l10n.i18n;


 /**
@@ -51,8 +51,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app"); //$NON-NLS-1$

     private JProgressBar progressBar;
     private JButton abortButton;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/SelectTemplatePanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/SelectTemplatePanel.java
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/SelectTemplatePanel.java
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -13,17 +13,17 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.ListSelectionModel;
+import thingamablog.l10n.i18n;

-import net.atlanticbb.tantlinger.i18n.I18n;


+
 public class SelectTemplatePanel extends JPanel
 {
     /**
      * 
      */
     private static final long serialVersionUID = 1L;
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");
     public static final String TEMPLATE_ZIP_PATH = "templateZip";  //  
@jve:decl-index=0: //$NON-NLS-1$
     public static final String TEMPLATE_NAME = "templateName"; //$NON-NLS-1$


Modified: trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/StatusBar.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/StatusBar.java      
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/StatusBar.java      
2008-04-22 23:39:58 UTC (rev 19512)
@@ -36,10 +36,10 @@
 import javax.swing.SwingConstants;
 import javax.swing.border.BevelBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.feed.Feed;
+import thingamablog.l10n.i18n;



@@ -55,8 +55,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     final static Icon blogIcon = UIUtils.getIcon(UIUtils.X16, "blog.png"); 
//$NON-NLS-1$
        final static Icon feedIcon = UIUtils.getIcon(UIUtils.X16, 
"blogpages.png"); //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBOptionsDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBOptionsDialog.java    
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBOptionsDialog.java    
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -66,12 +66,12 @@
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.gui.LabelledItemPanel;
 import net.sf.thingamablog.gui.MultilineText;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -85,8 +85,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app"); //$NON-NLS-1$

     private final String fontSizes[] = new String[] {"8", "9", "10", "11",  
//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
                                                                "12", "14", 
"16", "18", "20", "22", "24",  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 
//$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
@@ -165,12 +163,12 @@
                lafCombo.setModel(new DefaultComboBoxModel(lfNames));
                lafCombo.setSelectedItem(UIManager.getLookAndFeel().getName());

-        Vector locs = new 
Vector(Arrays.asList(I18n.getAvailableLanguagePackLocales()));
+        Vector locs = new 
Vector(Arrays.asList(i18n.getAvailableLanguagePackLocales()));
         Collections.sort(locs, new LocaleComparator());
-        if(!locs.contains(I18n.getLocale()))
-            locs.add(I18n.getLocale());
+        if(!locs.contains(i18n.getLocale()))
+            locs.add(i18n.getLocale());
         langCombo = new JComboBox(locs);        
-        langCombo.setSelectedItem(I18n.getLocale());
+        langCombo.setSelectedItem(i18n.getLocale());
         langCombo.setRenderer(new LocaleListCellRenderer());

                File dictDir = new File(TBGlobals.DICT_DIR);
@@ -473,7 +471,7 @@
        String msg = ""; //$NON-NLS-1$
        String curLaf = TBGlobals.getLookAndFeelClassName();
        String selLaf = lfinfo[lafCombo.getSelectedIndex()].getClassName();
-       if(!curLaf.equals(selLaf) || 
(!langCombo.getSelectedItem().equals(I18n.getLocale())))
+       if(!curLaf.equals(selLaf) || 
(!langCombo.getSelectedItem().equals(i18n.getLocale())))
        {       
                msg += i18n.str("look_and_feel_prompt"); //$NON-NLS-1$
        }

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBSearchDialog.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBSearchDialog.java 
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBSearchDialog.java 
2008-04-22 23:39:58 UTC (rev 19512)
@@ -48,7 +48,6 @@
 import javax.swing.border.BevelBorder;
 import javax.swing.border.EmptyBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.blog.BackendException;
 import net.sf.thingamablog.blog.BlogEntry;
 import net.sf.thingamablog.blog.CategoryEvent;
@@ -62,6 +61,7 @@
 import net.sf.thingamablog.feed.FeedSearch;
 import net.sf.thingamablog.gui.LabelledItemPanel;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;

 import com.tantlinger.jdatepicker.JCalendarComboBox;

@@ -77,8 +77,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     private final String DRAFTS = i18n.str("drafts"); //$NON-NLS-1$
        private final String POSTS = i18n.str("posts"); //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBViewerPaneModel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBViewerPaneModel.java  
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TBViewerPaneModel.java  
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -32,21 +32,19 @@
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.BlogEntry;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.feed.FeedItem;
 import net.sf.thingamablog.gui.ViewerPaneModel;
 import net.sf.thingamablog.gui.editor.EntryImageUtils;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
  */
 public class TBViewerPaneModel implements ViewerPaneModel
-{
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app"); //$NON-NLS-1$
-    
+{   
     private static final String exts[] = {".gif", ".jpg", ".png"};  
//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        private Object data;            
        private SimpleDateFormat sdf = new SimpleDateFormat("MMMM dd, yyyy 
hh:mm a z");  //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TaskDialog.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TaskDialog.java     
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TaskDialog.java     
2008-04-22 23:39:58 UTC (rev 19512)
@@ -30,12 +30,12 @@
 import javax.swing.JLabel;
 import javax.swing.JPanel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.PingProgress;
 import net.sf.thingamablog.blog.PublishProgress;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.transport.MailTransportProgress;
+import thingamablog.l10n.i18n;


 /**
@@ -48,8 +48,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app"); //$NON-NLS-1$

     private JButton closeButton, clearButton;
     private JComboBox blogCombo;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplatePropertiesPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplatePropertiesPanel.java
        2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplatePropertiesPanel.java
        2008-04-22 23:39:58 UTC (rev 19512)
@@ -18,9 +18,9 @@

 import javax.swing.BorderFactory;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.blog.TemplatePack;
+import thingamablog.l10n.i18n;


 /**
@@ -29,7 +29,6 @@
  */
 public class TemplatePropertiesPanel extends JPanel
 {    
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");
     private static final long serialVersionUID = 1L;

     private JLabel titleLabel = null;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplateSelectionPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplateSelectionPanel.java
 2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/TemplateSelectionPanel.java
 2008-04-22 23:39:58 UTC (rev 19512)
@@ -23,12 +23,12 @@
 import javax.swing.JList;
 import javax.swing.JPanel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.Author;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.blog.TemplatePack;
 import net.sf.thingamablog.blog.WeblogBackend;
+import thingamablog.l10n.i18n;


 /**
@@ -41,8 +41,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     private JComboBox tmplCombo; 
     private TemplatePropertiesPanel propertyPanel;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ThingamablogFrame.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ThingamablogFrame.java  
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/ThingamablogFrame.java  
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -67,7 +67,6 @@
 import javax.swing.tree.TreeModel;
 import javax.swing.tree.TreePath;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.io.IOUtils;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
@@ -113,6 +112,7 @@

 import com.l2fprod.common.swing.JDirectoryChooser;
 import freenet.utils.BrowserLaunch;
+import thingamablog.l10n.i18n;



@@ -128,7 +128,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app"); //$NON-NLS-1$
     private static Logger logger = 
Logger.getLogger("net.sf.thingamablog.gui.app"); //$NON-NLS-1$

     private final JFrame FRAME = this;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTableModel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTableModel.java   
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTableModel.java   
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -1,8 +1,8 @@
 package net.sf.thingamablog.gui.app;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.blog.BlogEntry;
 import net.sf.thingamablog.gui.table.DefaultSortTableModel;
+import thingamablog.l10n.i18n;


 public class WeblogTableModel extends DefaultSortTableModel
@@ -11,8 +11,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");

     public static final Object TITLE = i18n.str("title"); //$NON-NLS-1$
        public static final Object POST_DATE = i18n.str("date_posted"); 
//$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTreeModel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTreeModel.java    
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/app/WeblogTreeModel.java    
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -34,9 +34,9 @@
 import javax.swing.tree.TreeModel;
 import javax.swing.tree.TreePath;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.blog.WeblogList;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -45,9 +45,7 @@
  * 
  */
 public class WeblogTreeModel implements TreeModel
-{
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.app");
-    
+{   
        public static final String ROOT = i18n.str("my_sites"); //$NON-NLS-1$

        public static final String CURRENT = i18n.str("current"); //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/EntryEditor.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/EntryEditor.java 
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/EntryEditor.java 
2008-04-22 23:39:58 UTC (rev 19512)
@@ -73,7 +73,6 @@
 import javax.swing.text.html.HTMLEditorKit;
 import javax.swing.undo.UndoManager;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.DefaultAction;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.CompoundUndoManager;
@@ -129,6 +128,7 @@
 import org.dts.spell.swing.JTextComponentSpellChecker;

 import com.tantlinger.jdatepicker.JCalendarComboBox;
+import thingamablog.l10n.i18n;


 //TODO table edit actions on wysiwyg popup menu
@@ -144,8 +144,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.editor"); //$NON-NLS-1$

     public static final int NEW_ENTRY_MODE = -1;
     public static final int UPDATE_ENTRY_MODE = -2; 

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/HTMLEditor.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/HTMLEditor.java  
2008-04-22 22:52:29 UTC (rev 19511)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/gui/editor/HTMLEditor.java  
2008-04-22 23:39:58 UTC (rev 19512)
@@ -44,7 +44,6 @@
 import javax.swing.event.DocumentListener;
 import javax.swing.undo.UndoManager;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.CompoundUndoManager;
 import net.atlanticbb.tantlinger.ui.text.IndentationFilter;
@@ -66,6 +65,7 @@

 import org.bushe.swing.action.ActionList;
 import org.bushe.swing.action.ActionUIFactory;
+import thingamablog.l10n.i18n;


 /**
@@ -78,8 +78,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.editor");

     public static final int TEMPLATE_MODE = 1;
     public static final int FILE_MODE = 2;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/ASCIIPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/ASCIIPanel.java  
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/ASCIIPanel.java  
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -25,8 +25,8 @@
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
+import thingamablog.l10n.i18n;

 public class ASCIIPanel extends JPanel implements ActionListener
 {
@@ -34,8 +34,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private JScrollPane jScrollPane = null;


Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/EditableList.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/EditableList.java
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/EditableList.java
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -9,9 +9,10 @@
 import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
+import thingamablog.l10n.i18n;

-import net.atlanticbb.tantlinger.i18n.I18n;

+
 /*
  * Created on Jun 29, 2004
  *
@@ -48,8 +49,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private JList list;
        private JButton addButton;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBArchivingPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBArchivingPanel.java
        2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBArchivingPanel.java
        2008-04-22 23:39:58 UTC (rev 19512)
@@ -40,13 +40,13 @@
 import javax.swing.SpinnerNumberModel;
 import javax.swing.border.TitledBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.gui.LabelledItemPanel;

 import com.tantlinger.jdatepicker.JCalendarComboBox;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -60,8 +60,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private static final String MONTHLY = i18n.str("monthly"); //$NON-NLS-1$
        private static final String WEEKLY = i18n.str("weekly"); //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCategoriesPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCategoriesPanel.java
       2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCategoriesPanel.java
       2008-04-22 23:39:58 UTC (rev 19512)
@@ -35,10 +35,10 @@
 import javax.swing.JSpinner;
 import javax.swing.SpinnerNumberModel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.gui.LabelledItemPanel;
+import thingamablog.l10n.i18n;



@@ -55,8 +55,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private TBWeblog weblog;
        private EditableList eCatList;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCustomVariablesPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCustomVariablesPanel.java
  2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBCustomVariablesPanel.java
  2008-04-22 23:39:58 UTC (rev 19512)
@@ -36,11 +36,11 @@
 import javax.swing.JTextArea;
 import javax.swing.JTextField;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.generator.CustomTag;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;


 /**
@@ -55,8 +55,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private TBWeblog weblog;
        private EditableList eList;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBEmailPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBEmailPanel.java
    2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBEmailPanel.java
    2008-04-22 23:39:58 UTC (rev 19512)
@@ -18,9 +18,9 @@
 import javax.swing.JTextField;
 import javax.swing.SpinnerNumberModel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.blog.Weblog;
+import thingamablog.l10n.i18n;


 /**
@@ -34,8 +34,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties"); //$NON-NLS-1$

     private static final String[] PROTOCOLS = {"POP3", "IMAP"}; //$NON-NLS-1$ 
//$NON-NLS-2$
     private JLabel protocolLabel = null;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogNodeWizardDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogNodeWizardDialog.java
  2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogNodeWizardDialog.java
  2008-04-22 23:39:58 UTC (rev 19512)
@@ -11,7 +11,6 @@

 import java.awt.BorderLayout;
 import java.awt.CardLayout;
-import java.awt.Color;
 import java.awt.FlowLayout;
 import java.awt.Frame;
 import java.awt.GridLayout;
@@ -30,11 +29,8 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
-import javax.swing.SwingConstants;
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.EtchedBorder;
-import net.atlanticbb.tantlinger.i18n.I18n;
-import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.BackendException;

@@ -45,6 +41,7 @@
 import net.sf.thingamablog.gui.MultilineText;
 import net.sf.thingamablog.transport.FCPTransport;
 import net.sf.thingamablog.util.freenet.fcp.fcpManager;
+import thingamablog.l10n.i18n;

 /**
  *
@@ -52,7 +49,6 @@
  */
 public class TBFlogNodeWizardDialog extends JDialog {

-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");
     private Logger logger = 
Logger.getLogger("net.sf.thingamablog.gui.properties");

     private PropertyPanel nodePanel;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogWizardDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogWizardDialog.java
      2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFlogWizardDialog.java
      2008-04-22 23:39:58 UTC (rev 19512)
@@ -12,10 +12,8 @@
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.File;
-import java.io.IOException;
 import java.util.List;
 import java.util.Vector;
-import java.util.logging.Level;
 import java.util.logging.Logger;

 import javax.swing.BorderFactory;
@@ -31,10 +29,8 @@
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.EtchedBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
-import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.Author;
 import net.sf.thingamablog.blog.BackendException;
 import net.sf.thingamablog.blog.TBWeblog;
@@ -48,7 +44,7 @@
 import net.sf.thingamablog.transport.FCPTransport;
 import net.sf.thingamablog.transport.LocalTransport;
 import net.sf.thingamablog.util.freenet.fcp.fcpManager;
-import net.sf.thingamablog.util.string.ASCIIconv;
+import thingamablog.l10n.i18n;



@@ -65,7 +61,6 @@
      */
     private static final long serialVersionUID = 1L;

-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties"); //$NON-NLS-1$
     private Logger logger = 
Logger.getLogger("net.sf.thingamablog.gui.properties");

     private static final String CANCEL = i18n.str("cancel"); //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFrontPagePanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFrontPagePanel.java
        2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBFrontPagePanel.java
        2008-04-22 23:39:58 UTC (rev 19512)
@@ -37,10 +37,10 @@
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.TitledBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.gui.LabelledItemPanel;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -54,8 +54,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private TBWeblog weblog;


Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBPublishTransportPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBPublishTransportPanel.java
 2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBPublishTransportPanel.java
 2008-04-22 23:39:58 UTC (rev 19512)
@@ -28,13 +28,10 @@
 import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.util.Iterator;
 import java.util.SortedMap;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.swing.JButton;

@@ -47,7 +44,6 @@
 import javax.swing.JTextField;
 import javax.swing.border.TitledBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
 import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.TBWeblog;
@@ -60,6 +56,7 @@
 import net.sf.thingamablog.transport.SFTPTransport;
 import net.sf.thingamablog.util.freenet.fcp.fcpManager;
 import net.sf.thingamablog.util.string.ASCIIconv;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -73,8 +70,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private final String FTP = "FTP"; //$NON-NLS-1$
        private final String SFTP = "SFTP"; //$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBTemplatesPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBTemplatesPanel.java
        2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBTemplatesPanel.java
        2008-04-22 23:39:58 UTC (rev 19512)
@@ -9,11 +9,11 @@
 import javax.swing.BorderFactory;
 import javax.swing.JLabel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.blog.TemplatePack;
 import net.sf.thingamablog.gui.app.TemplateSelectionPanel;
+import thingamablog.l10n.i18n;


 /**
@@ -26,8 +26,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private TemplateSelectionPanel tmplPanel;
     private TBWeblog blog;

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWeblogPropertiesDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWeblogPropertiesDialog.java
        2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWeblogPropertiesDialog.java
        2008-04-22 23:39:58 UTC (rev 19512)
@@ -26,10 +26,10 @@
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -42,8 +42,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties"); //$NON-NLS-1$

     private TBWeblog tbw;
        private Vector opts = new Vector();

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWizardDialog.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWizardDialog.java
  2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/TBWizardDialog.java
  2008-04-22 23:39:58 UTC (rev 19512)
@@ -12,16 +12,13 @@
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.File;
-import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.List;
 import java.util.Vector;
-import java.util.logging.Logger;

 import javax.swing.BorderFactory;
 import javax.swing.JButton;
-import javax.swing.JComboBox;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
@@ -33,10 +30,8 @@
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.EtchedBorder;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.atlanticbb.tantlinger.ui.UIUtils;
 import net.atlanticbb.tantlinger.ui.text.TextEditPopupManager;
-import net.sf.thingamablog.TBGlobals;
 import net.sf.thingamablog.blog.Author;
 import net.sf.thingamablog.blog.BackendException;
 import net.sf.thingamablog.blog.TBWeblog;
@@ -47,8 +42,7 @@
 import net.sf.thingamablog.gui.MultilineText;
 import net.sf.thingamablog.gui.app.TemplateSelectionPanel;
 import net.sf.thingamablog.gui.app.WeblogPreviewer;
-import net.sf.thingamablog.util.freenet.fcp.fcpManager;
-import net.sf.thingamablog.util.string.ASCIIconv;
+import thingamablog.l10n.i18n;



@@ -65,8 +59,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties"); //$NON-NLS-1$

     private static final String CANCEL = i18n.str("cancel"); //$NON-NLS-1$
        private static final String FINISH = i18n.str("finish");         
//$NON-NLS-1$

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogEditableListModel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogEditableListModel.java
 2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogEditableListModel.java
 2008-04-22 23:39:58 UTC (rev 19512)
@@ -27,11 +27,11 @@
 import javax.swing.JOptionPane;
 import javax.swing.JTextField;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.blog.Author;
 import net.sf.thingamablog.blog.BackendException;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.gui.LabelledItemPanel;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -41,9 +41,7 @@
  */
 public class WeblogEditableListModel implements EditableListModel
 {
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");
-    
-    public static final int CATEGORIES = -1;
+        public static final int CATEGORIES = -1;
        public static final int AUTHORS = -2;
        private int mode;
        private Vector listEdits = new Vector();

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogPropertiesDialogFactory.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogPropertiesDialogFactory.java
   2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/WeblogPropertiesDialogFactory.java
   2008-04-22 23:39:58 UTC (rev 19512)
@@ -5,10 +5,10 @@
 package net.sf.thingamablog.gui.properties;
 import java.awt.Frame;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.blog.TBWeblog;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.gui.StandardDialog;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -16,9 +16,7 @@
  * Creates and shows the appropriate weblog properties dialog
  */
 public class WeblogPropertiesDialogFactory
-{
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");
-    
+{   
     /**
         * Shows a weblog property dialog box
         * @param wb - The weblog

Modified: 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/XmlRpcPingPanel.java
===================================================================
--- 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/XmlRpcPingPanel.java
 2008-04-22 22:52:29 UTC (rev 19511)
+++ 
trunk/apps/thingamablog/src/net/sf/thingamablog/gui/properties/XmlRpcPingPanel.java
 2008-04-22 23:39:58 UTC (rev 19512)
@@ -41,11 +41,11 @@
 import javax.swing.table.TableColumn;
 import javax.swing.table.TableModel;

-import net.atlanticbb.tantlinger.i18n.I18n;
 import net.sf.thingamablog.blog.PingService;
 import net.sf.thingamablog.blog.Weblog;
 import net.sf.thingamablog.blog.WeblogsDotComPing;
 import net.sf.thingamablog.gui.LabelledItemPanel;
+import thingamablog.l10n.i18n;

 /**
  * @author Bob Tantlinger
@@ -59,8 +59,6 @@
      * 
      */
     private static final long serialVersionUID = 1L;
-
-    private static final I18n i18n = 
I18n.getInstance("net.sf.thingamablog.gui.properties");

     private Weblog weblog;
        private PingerTableModel model;

Added: trunk/apps/thingamablog/src/net/sf/thingamablog/util/io/FileUtil.java
===================================================================
--- trunk/apps/thingamablog/src/net/sf/thingamablog/util/io/FileUtil.java       
                        (rev 0)
+++ trunk/apps/thingamablog/src/net/sf/thingamablog/util/io/FileUtil.java       
2008-04-22 23:39:58 UTC (rev 19512)
@@ -0,0 +1,295 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+
+package net.sf.thingamablog.util.io;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+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.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.logging.Level;
+
+import java.util.logging.Logger;
+import net.sf.thingamablog.transport.DefaultMIMETypes;
+
+final public class FileUtil {
+    
+    private static final int BUFFER_SIZE = 4096;
+    private static Logger logger = 
Logger.getLogger("net.sf.thingamablog.util.io");
+    
+    /** Round up a value to the next multiple of a power of 2 */
+    private static final long roundup_2n(long val, int blocksize) {
+        int mask=blocksize-1;
+        return (val+mask)&~mask;
+    }
+    
+    /**
+     * Guesstimate real disk usage for a file with a given filename, of a 
given length.
+     */
+    public static long estimateUsage(File file, long flen) {
+        /**
+         * It's possible that none of these assumptions are accurate for any 
filesystem;
+         * this is intended to be a plausible worst case.
+         */
+        // Assume 4kB clusters for calculating block usage (NTFS)
+        long blockUsage = roundup_2n(flen, 4096);
+        // Assume 512 byte filename entries, with 100 bytes overhead, for 
filename overhead (NTFS)
+        String filename = file.getName();
+        int nameLength = filename.getBytes().length + 100;
+        long filenameUsage = roundup_2n(nameLength, 512);
+        // Assume 50 bytes per block tree overhead with 1kB blocks (reiser3 
worst case)
+        long extra = (roundup_2n(flen, 1024) / 1024) * 50;
+        return blockUsage + filenameUsage + extra;
+    }
+    
+    /**
+     *  Is possParent a parent of filename?
+     * Why doesn't java provide this? :(
+     * */
+    public static boolean isParent(File poss, File filename) {
+        File canon = FileUtil.getCanonicalFile(poss);
+        File canonFile = FileUtil.getCanonicalFile(filename);
+        
+        if(isParentInner(poss, filename)) return true;
+        if(isParentInner(poss, canonFile)) return true;
+        if(isParentInner(canon, filename)) return true;
+        if(isParentInner(canon, canonFile)) return true;
+        return false;
+    }
+    
+    private static boolean isParentInner(File possParent, File filename) {
+        while(true) {
+            if(filename.equals(possParent)) return true;
+            filename = filename.getParentFile();
+            if(filename == null) return false;
+        }
+    }
+    
+    public static File getCanonicalFile(File file) {
+        File result;
+        try {
+            result = file.getCanonicalFile();
+        } catch (IOException e) {
+            result = file.getAbsoluteFile();
+        }
+        return result;
+    }
+    
+    public static String readUTF(File file) throws FileNotFoundException, 
IOException {
+        return readUTF(file, 0);
+    }
+    
+    public static String readUTF(File file, long offset) throws 
FileNotFoundException, IOException {
+        StringBuffer result = new StringBuffer();
+        FileInputStream fis = null;
+        BufferedInputStream bis = null;
+        InputStreamReader isr = null;
+        
+        try {
+            fis = new FileInputStream(file);
+            skipFully(fis, offset);
+            bis = new BufferedInputStream(fis);
+            isr = new InputStreamReader(bis, "UTF-8");
+            
+            char[] buf = new char[4096];
+            int length = 0;
+            
+            while((length = isr.read(buf)) > 0) {
+                result.append(buf, 0, length);
+            }
+            
+        } finally {
+            try {
+                if(isr != null) isr.close();
+                if(bis != null) bis.close();
+                if(fis != null) fis.close();
+            } catch (IOException e) {}
+        }
+        return result.toString();
+    }
+    
+    /**
+     * Reliably skip a number of bytes or throw.
+     */
+    public static void skipFully(InputStream is, long skip) throws IOException 
{
+        long skipped = 0;
+        while(skipped < skip) {
+            long x = is.skip(skip - skipped);
+            if(x <= 0) throw new IOException("Unable to skip "+(skip - 
skipped)+" bytes");
+            skipped += x;
+        }
+    }
+    
+    public static boolean writeTo(InputStream input, File target) throws 
FileNotFoundException, IOException {
+        DataInputStream dis = null;
+        FileOutputStream fos = null;
+        File file = File.createTempFile("temp", ".tmp", 
target.getParentFile());
+        logger.log(Level.INFO, "Writing to "+file+" to be renamed to "+target);
+        
+        try {
+            dis = new DataInputStream(input);
+            fos = new FileOutputStream(file);
+            
+            int len = 0;
+            byte[] buffer = new byte[4096];
+            while ((len = dis.read(buffer)) > 0) {
+                fos.write(buffer, 0, len);
+            }
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            if(dis != null) dis.close();
+            if(fos != null) fos.close();
+        }
+        
+        if(FileUtil.renameTo(file, target))
+            return true;
+        else {
+            file.delete();
+            return false;
+        }
+    }
+    
+    public static boolean renameTo(File orig, File dest) {
+        // Try an atomic rename
+        // Shall we prevent symlink-race-conditions here ?
+        if(orig.equals(dest))
+            throw new IllegalArgumentException("Huh? the two file descriptors 
are the same!");
+        if(!orig.exists()) {
+            throw new IllegalArgumentException("Original doesn't exist!");
+        }
+        if (!orig.renameTo(dest)) {
+            // Not supported on some systems (Windows)
+            if (!dest.delete()) {
+                if (dest.exists()) {
+                    logger.log(Level.SEVERE, "Could not delete " + dest + " - 
check permissions");
+                }
+            }
+            if (!orig.renameTo(dest)) {
+                logger.log(Level.SEVERE, "Could not rename " + orig + " to " + 
dest +
+                        (dest.exists() ? " (target exists)" : "") +
+                        (orig.exists() ? " (source exists)" : "") +
+                        " - check permissions");
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    public static String sanitize(String s) {
+        StringBuffer sb = new StringBuffer(s.length());
+        for(int i=0;i<s.length();i++) {
+            char c = s.charAt(i);
+            if((c == '/') || (c == '\\') || (c == '%') || (c == '>') || (c == 
'<') || (c == ':') || (c == '\'') || (c == '\"'))
+                continue;
+            if(Character.isDigit(c))
+                sb.append(c);
+            else if(Character.isLetter(c))
+                sb.append(c);
+            else if(Character.isWhitespace(c))
+                sb.append(' ');
+            else if((c == '-') || (c == '_') || (c == '.'))
+                sb.append(c);
+        }
+        return sb.toString();
+    }
+    
+    public static String sanitize(String filename, String mimeType) {
+        filename = sanitize(filename);
+        if(mimeType == null) return filename;
+        if(filename.indexOf('.') >= 0) {
+            String oldExt = filename.substring(filename.lastIndexOf('.'));
+            if(DefaultMIMETypes.isValidExt(mimeType, oldExt)) return filename;
+        }
+        String defaultExt = DefaultMIMETypes.getExtension(filename);
+        if(defaultExt == null) return filename;
+        else return filename + '.' + defaultExt;
+    }
+    
+    /**
+     * Find the length of an input stream. This method will consume the 
complete
+     * input stream until its {@link InputStream#read(byte[])} method returns
+     * <code>-1</code>, thus signalling the end of the stream.
+     *
+     * @param source
+     *            The input stream to find the length of
+     * @return The numbe of bytes that can be read from the stream
+     * @throws IOException
+     *             if an I/O error occurs
+     */
+    public static long findLength(InputStream source) throws IOException {
+        long length = 0;
+        byte[] buffer = new byte[BUFFER_SIZE];
+        int read = 0;
+        while (read > -1) {
+            read = source.read(buffer);
+            if (read != -1) {
+                length += read;
+            }
+        }
+        return length;
+    }
+    
+    /**
+     * Copies <code>length</code> bytes from the source input stream to the
+     * destination output stream. If <code>length</code> is <code>-1</code>
+     * as much bytes as possible will be copied (i.e. until
+     * {@link InputStream#read()} returns <code>-1</code> to signal the end of
+     * the stream).
+     *
+     * @param source
+     *            The input stream to read from
+     * @param destination
+     *            The output stream to write to
+     * @param length
+     *            The number of bytes to copy
+     * @throws IOException
+     *             if an I/O error occurs
+     */
+    public static void copy(InputStream source, OutputStream destination, long 
length) throws IOException {
+        long remaining = length;
+        byte[] buffer = new byte[BUFFER_SIZE];
+        int read = 0;
+        while ((remaining == -1) || (remaining > 0)) {
+            read = source.read(buffer, 0, ((remaining > BUFFER_SIZE) || 
(remaining == -1)) ? BUFFER_SIZE : (int) remaining);
+            if (read == -1) {
+                if (length == -1) {
+                    return;
+                }
+                throw new EOFException("stream reached eof");
+            }
+            destination.write(buffer, 0, read);
+            remaining -= read;
+        }
+    }
+    
+    /** Delete everything in a directory. Only use this when we are *very 
sure* there is no
+     * important data below it! */
+    public static boolean removeAll(File wd) {
+        if(!wd.isDirectory()) {
+            System.err.println("DELETING FILE "+wd);
+            if(!wd.delete() && wd.exists()) {
+                logger.log(Level.SEVERE, "Could not delete file: "+wd);
+                return false;
+            }
+        } else {
+            File[] subfiles = wd.listFiles();
+            for(int i=0;i<subfiles.length;i++) {
+                if(!removeAll(subfiles[i])) return false;
+            }
+            if(!wd.delete()) {
+                logger.log(Level.SEVERE, "Could not delete directory: "+wd);
+            }
+        }
+        return true;
+    }
+    
+}

Added: trunk/apps/thingamablog/src/thingamablog/l10n/FSParseException.java
===================================================================
--- trunk/apps/thingamablog/src/thingamablog/l10n/FSParseException.java         
                (rev 0)
+++ trunk/apps/thingamablog/src/thingamablog/l10n/FSParseException.java 
2008-04-22 23:39:58 UTC (rev 19512)
@@ -0,0 +1,21 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+
+package thingamablog.l10n;
+
+/**
+ * Exception thrown when we cannot parse a supplied peers file in
+ * SimpleFieldSet format (after it has been turned into a SFS).
+ */
+public class FSParseException extends Exception {
+    private static final long serialVersionUID = -1;
+    public FSParseException(Exception e) {
+        super(e);
+    }
+    
+    public FSParseException(String msg) {
+        super(msg);
+    }
+    
+}

Added: trunk/apps/thingamablog/src/thingamablog/l10n/Fields.java
===================================================================
--- trunk/apps/thingamablog/src/thingamablog/l10n/Fields.java                   
        (rev 0)
+++ trunk/apps/thingamablog/src/thingamablog/l10n/Fields.java   2008-04-22 
23:39:58 UTC (rev 19512)
@@ -0,0 +1,752 @@
+package thingamablog.l10n;
+
+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;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * 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' };
+    
+    private static Logger logger = 
Logger.getLogger("net.sf.thingamablog.util.io");
+    
+    /**
+     * 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"));
+    }
+    
+    /**
+     * Find the boolean value of the field. Throw if the string is neither 
"yes"/"true" nor "no"/"false".
+     * @param s
+     * @return
+     */
+    public static boolean stringToBool(String s) throws NumberFormatException {
+        if(s == null) throw new NumberFormatException("Null");
+        if(s.equalsIgnoreCase("false") || s.equalsIgnoreCase("no")) return 
false;
+        if(s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes")) return 
true;
+        throw new NumberFormatException("Invalid boolean: "+s);
+    }
+    
+    /**
+     * 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) {
+        if(ls == null) return null;
+        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;
+    }
+    
+    /**
+     * Long version of above Not believed to be secure in any sense of the 
word :)
+     */
+    public static final long longHashCode(byte[] b, int offset, int length) {
+        long h = 0;
+        for (int i = length - 1; i >= 0; --i) {
+            int x = b[i+offset] & 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.
+     */
+    public static long[] bytesToLongs(byte[] buf) {
+        return bytesToLongs(buf, 0, buf.length);
+    }
+    
+    /**
+     * Convert an array of bytes to an array of longs.
+     * @param buf
+     * @param length
+     * @param offset
+     * @return
+     */
+    public static long[] bytesToLongs(byte[] buf, int offset, int length) {
+        if(length % 8 != 0) throw new IllegalArgumentException();
+        long[] longs = new long[length/8];
+        for(int i=0;i<longs.length;i++) {
+            long x = 0;
+            for(int j=7;j>=0;j--) {
+                long y = (buf[offset+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 int[] bytesToInts(byte[] buf, int offset, int length) {
+        if(length % 4 != 0) throw new IllegalArgumentException();
+        int[] ints = new int[length/4];
+        for(int i=0;i<ints.length;i++) {
+            int x = 0;
+            for(int j=3;j>=0;j--) {
+                int y = (buf[j+offset] & 0xff);
+                x = (x << 8) | y;
+            }
+            ints[i] = x;
+        }
+        return ints;
+    }
+    
+    public static int[] bytesToInts(byte[] buf) {
+        return bytesToInts(buf, 0, buf.length);
+    }
+    
+    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;
+    }
+    
+    public static long parseLong(String s, long defaultValue) {
+        try {
+            return Long.parseLong(s);
+        } catch (NumberFormatException e) {
+            logger.log(Level.SEVERE, "Failed to parse value as long: "+s+" : 
"+e, e);
+            return defaultValue;
+        }
+    }
+    
+    public static int parseInt(String s, int defaultValue) {
+        try {
+            return Integer.parseInt(s);
+        } catch (NumberFormatException e) {
+            logger.log(Level.SEVERE, "Failed to parse value as int: "+s+" : 
"+e, e);
+            return defaultValue;
+        }
+    }
+    
+    public static long parseShort(String s, short defaultValue) {
+        try {
+            return Short.parseShort(s);
+        } catch (NumberFormatException e) {
+            logger.log(Level.SEVERE, "Failed to parse value as short: "+s+" : 
"+e, e);
+            return defaultValue;
+        }
+    }
+    
+    /**
+     * Parse a human-readable string possibly including SI units into a short.
+     * @throws NumberFormatException
+     *             if the string is not parseable
+     */
+    public static short parseSIShort(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 parseSIInt(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 parseSILong(String s) throws NumberFormatException {
+        long res = 1;
+        int x = s.length() - 1;
+        int idx;
+        try {
+            long[] l =
+            {
+                1000,
+                1 << 10,
+                1000 * 1000,
+                1 << 20,
+                1000l * 1000l * 1000l,
+                1l << 30,
+                1000l * 1000l * 1000l * 1000l,
+                1l << 40,
+                1000l * 1000l * 1000l * 1000l * 1000,
+                1l << 50,
+                1000l * 1000l * 1000l * 1000l * 1000l * 1000l,
+                1l << 60 };
+            while ((x >= 0)
+            && ((idx = "kKmMgGtTpPeE".indexOf(s.charAt(x))) != -1)) {
+                x--;
+                res *= l[idx];
+            }
+            String multiplier = s.substring(0, x + 1).trim();
+            if(multiplier.indexOf('.') > -1 || multiplier.indexOf('E') > -1) {
+                res *= Double.parseDouble(multiplier);
+//                             if(Logger.shouldLog(Logger.MINOR, 
Fields.class)) Logger.minor(Fields.class, "Parsed "+multiplier+" of "+s+" as 
double: "+res);
+            } else {
+                res *= Long.parseLong(multiplier);
+//                             if(Logger.shouldLog(Logger.MINOR, 
Fields.class)) Logger.minor(Fields.class, "Parsed "+multiplier+" of "+s+" as 
long: "+res);
+            }
+        } catch (ArithmeticException e) {
+            res = Long.MAX_VALUE;
+            throw new NumberFormatException(e.getMessage());
+        }
+        return res;
+    }
+    
+    public static double[] bytesToDoubles(byte[] data, int offset, int length) 
{
+        long[] longs = bytesToLongs(data, offset, length);
+        double[] doubles = new double[longs.length];
+        for(int i=0;i<longs.length;i++)
+            doubles[i] = Double.longBitsToDouble(longs[i]);
+        return doubles;
+    }
+    
+    public static byte[] doublesToBytes(double[] doubles) {
+        long[] longs = new long[doubles.length];
+        for(int i=0;i<longs.length;i++)
+            longs[i] = Double.doubleToLongBits(doubles[i]);
+        return longsToBytes(longs);
+    }
+    
+    public static double[] bytesToDoubles(byte[] data) {
+        return bytesToDoubles(data, 0, data.length);
+    }
+    
+    /**
+     * Assumes the array is sorted in ascending order, [begin] is lowest and 
[end] is highest.
+     */
+    public static int binarySearch(long[] values, long key, int origBegin, int 
origEnd) {
+        int begin = origBegin;
+        int end = origEnd;
+        while(true) {
+            if(end < begin)    // so we can use origEnd=length-1 without 
worrying length=0
+                return -begin-1;
+            
+            int middle = (begin + end) >>> 1;
+            if(values[middle] == key)
+                return middle;
+            
+            if(values[middle] > key) {
+                end = middle - 1;
+            } else if(values[middle] < key) {
+                begin = middle + 1;
+            }
+        }
+    }
+    
+}
+

Added: trunk/apps/thingamablog/src/thingamablog/l10n/L10n.java
===================================================================
--- trunk/apps/thingamablog/src/thingamablog/l10n/L10n.java                     
        (rev 0)
+++ trunk/apps/thingamablog/src/thingamablog/l10n/L10n.java     2008-04-22 
23:39:58 UTC (rev 19512)
@@ -0,0 +1,357 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+
+package thingamablog.l10n;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.logging.Level;
+
+import java.util.logging.Logger;
+import net.sf.thingamablog.util.io.Closer;
+import thingamablog.l10n.SimpleFieldSet;
+import net.sf.thingamablog.util.io.FileUtil;
+
+
+/**
+ * This class provides a trivial internationalization framework to a Freenet 
node.
+ *
+ * @author Florent Daigni&egrave;re &lt;nextgens at freenetproject.org&gt;
+ *
+ * TODO: Maybe base64 the override file ?
+ *
+ * comment(mario): for www interface we might detect locale from http requests?
+ * for other access (telnet) using system locale would probably be good, but
+ * it would be nice to have a command to switch locale on the fly.
+ */
+public class L10n {
+    public static final String CLASS_NAME = "l10n";
+    public static final String PREFIX = "thingamablog.l10n.";
+    public static final String SUFFIX = ".properties";
+    public static final String OVERRIDE_SUFFIX = ".override" + SUFFIX;
+    
+    public static final String FALLBACK_DEFAULT = "en";
+    public static final String[] AVAILABLE_LANGUAGES = { "en", "de", "fr", 
"es", "ja"};
+    private final String selectedLanguage;
+    
+    private static SimpleFieldSet currentTranslation = null;
+    private static SimpleFieldSet fallbackTranslation = null;
+    private static L10n currentClass = null;
+    
+    private static SimpleFieldSet translationOverride;
+    private static final Object sync = new Object();
+    
+    private static Logger logger = 
Logger.getLogger("net.sf.thingamablog.L10n");
+    
+    L10n(String selected) {
+        selectedLanguage = selected;
+        File tmpFile = new File(L10n.PREFIX + selected + L10n.OVERRIDE_SUFFIX);
+        
+        try {
+            if(tmpFile.exists() && tmpFile.canRead() && tmpFile.length() > 0) {
+                logger.log(Level.INFO, "Override file detected : let's try to 
load it");
+                translationOverride = SimpleFieldSet.readFrom(tmpFile, false, 
false);
+            } else {
+                // try to restore a backup
+                File backup = new File(tmpFile.getParentFile(), 
tmpFile.getName()+".bak");
+                if(backup.exists() && backup.length() > 0) {
+                    logger.log(Level.INFO, "Override-backup file detected : 
let's try to load it");
+                    translationOverride = SimpleFieldSet.readFrom(backup, 
false, false);
+                }
+                translationOverride = null;
+            }
+            
+        } catch (IOException e) {
+            translationOverride = null;
+            logger.log(Level.SEVERE, "IOError while accessing the file!" + 
e.getMessage(), e);
+        }
+        currentTranslation = loadTranslation(selectedLanguage);
+        if(currentTranslation == null) {
+            logger.log(Level.SEVERE, "The translation file for " + 
selectedLanguage + " is invalid. The node will load an empty template.");
+            currentTranslation = null;
+            translationOverride = new SimpleFieldSet(false);
+        }
+    }
+    
+    /**
+     * Set the default language used by the framework.
+     *
+     * @param selectedLanguage (2 letter code)
+     * @throws MissingResourceException
+     */
+    public static void setLanguage(String selectedLanguage) throws 
MissingResourceException {
+        synchronized (sync) {
+            for(int i=0; i<AVAILABLE_LANGUAGES.length; i++){
+                if(selectedLanguage.equalsIgnoreCase(AVAILABLE_LANGUAGES[i])){
+                    selectedLanguage = AVAILABLE_LANGUAGES[i];
+                    logger.log(Level.INFO, "Changing the current language to : 
" + selectedLanguage);
+                    
+                    currentClass = new L10n(selectedLanguage);
+                    
+                    if(currentTranslation == null) {
+                        currentClass = new L10n(FALLBACK_DEFAULT);
+                        throw new MissingResourceException("Unable to load the 
translation file for "+selectedLanguage, "l10n", selectedLanguage);
+                    }
+                    
+                    return;
+                }
+            }
+            
+            currentClass = new L10n(FALLBACK_DEFAULT);
+            logger.log(Level.SEVERE, "The requested translation is not 
available!" + selectedLanguage);
+            throw new MissingResourceException("The requested translation 
("+selectedLanguage+") hasn't been found!", CLASS_NAME, selectedLanguage);
+        }
+    }
+    
+    public static void setOverride(String key, String value) {
+        key = key.trim();
+        value = value.trim();
+        synchronized (sync) {
+            // Is the override already declared ? if not, create it.
+            if(translationOverride == null)
+                translationOverride = new SimpleFieldSet(false);
+            
+            // If there is no need to keep it in the override, remove it...
+            // unless the original/default is the same as the translation
+            if(("".equals(value) || L10n.getString(key).equals(value)) && 
!L10n.getDefaultString(key).equals(value)) {
+                translationOverride.removeValue(key);
+            } else {
+                value = value.replaceAll("(\r|\n|\t)+", "");
+                
+                // Set the value of the override
+                translationOverride.putOverwrite(key, value);
+                logger.log(Level.INFO, "Got a new translation key: set the 
Override!");
+            }
+            
+            // Save the file to disk
+            _saveTranslationFile();
+        }
+    }
+    
+    private static void _saveTranslationFile() {
+        FileOutputStream fos = null;
+        File finalFile = new File(L10n.PREFIX + L10n.getSelectedLanguage() + 
L10n.OVERRIDE_SUFFIX);
+        
+        try {
+            // We don't set deleteOnExit on it : if the save operation fails, 
we want a backup
+            File tempFile = new File(finalFile.getParentFile(), 
finalFile.getName()+".bak");
+            logger.log(Level.INFO, "The temporary filename is : " + tempFile);
+            
+            fos = new FileOutputStream(tempFile);
+            L10n.translationOverride.writeTo(fos);
+            
+            FileUtil.renameTo(tempFile, finalFile);
+            logger.log(Level.INFO, "Override file saved successfully!");
+        } catch (IOException e) {
+            logger.log(Level.SEVERE, "Error while saving the translation 
override: "+ e.getMessage(), e);
+        } finally {
+            Closer.close(fos);
+        }
+    }
+    
+    /**
+     * Return a new copy of the current translation file
+     *
+     * @return SimpleFieldSet or null
+     */
+    public static SimpleFieldSet getCurrentLanguageTranslation() {
+        synchronized (sync) {
+            return (currentTranslation == null ? null : new 
SimpleFieldSet(currentTranslation));
+        }
+    }
+    
+    /**
+     * Return a copy of the current translation override if it exists or null
+     *
+     * @return SimpleFieldSet or null
+     */
+    public static SimpleFieldSet getOverrideForCurrentLanguageTranslation() {
+        synchronized (sync) {
+            return (translationOverride == null ? null : new 
SimpleFieldSet(translationOverride));
+        }
+    }
+    
+    /**
+     * Return a copy of the default translation file (english one)
+     *
+     * @return SimpleFieldSet
+     */
+    public static SimpleFieldSet getDefaultLanguageTranslation() {
+        synchronized (sync) {
+            if(fallbackTranslation == null)
+                fallbackTranslation = loadTranslation(FALLBACK_DEFAULT);
+            
+            return new SimpleFieldSet(fallbackTranslation);
+        }
+    }
+    
+    /**
+     * The real meat
+     *
+     * Same thing as getString(key, false);
+     * Ensure it will *always* return a String value.
+     *
+     * @param key
+     * @return the translated string or the default value from the default 
language or the key if nothing is found
+     */
+    public static String getString(String key) {
+        return getString(key, false);
+    }
+    
+    /**
+     * You probably don't want to use that one directly
+     * @see getString(String)
+     */
+    public static String getString(String key, boolean returnNullIfNotFound) {
+        String result = null;
+        synchronized (sync) {
+            if(translationOverride != null)
+                result = translationOverride.get(key);
+        }
+        if(result != null) return result;
+        
+        synchronized (sync) {
+            if(currentTranslation != null)
+                result = currentTranslation.get(key);
+        }
+        if(result != null)
+            return result;
+        else {
+            logger.log(Level.INFO, "The translation for " + key + " hasn't 
been found ("+getSelectedLanguage()+")! please tell the maintainer.");
+            return (returnNullIfNotFound ? null : getDefaultString(key));
+        }
+    }
+    
+    /**
+     * Return the english translation of the key or the key itself if it 
doesn't exist.
+     *
+     * @param key
+     * @return String
+     */
+    public static String getDefaultString(String key) {
+        String result = null;
+        // We instanciate it only if necessary
+        synchronized (sync) {
+            if(fallbackTranslation == null)
+                fallbackTranslation = loadTranslation(FALLBACK_DEFAULT);
+            result = fallbackTranslation.get(key);
+        }
+        
+        if(result != null) {
+            return result;
+        }
+        logger.log(Level.SEVERE, "The default translation for " + key + " 
hasn't been found!");
+        System.err.println("The default translation for " + key + " hasn't 
been found!");
+        new Exception().printStackTrace();
+        return key;
+    }
+    
+    /**
+     * Allows things like :
+     * L10n.getString("testing.test", new String[]{ "test1", "test2" }, new 
String[] { "a", "b" })
+     *
+     * @param key
+     * @param patterns : a list of patterns wich are matchable from the 
translation
+     * @param values : the values corresponding to the list
+     * @return the translated string or the default value from the default 
language or the key if nothing is found
+     */
+    public static String getString(String key, String[] patterns, String[] 
values) {
+        assert(patterns.length == values.length);
+        String result = getString(key);
+        
+        for(int i=0; i<patterns.length; i++)
+            result = result.replaceAll("\\$\\{"+patterns[i]+"\\}", 
quoteReplacement(values[i]));
+        
+        return result;
+    }
+    
+    private static String quoteReplacement(String s) {
+        if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1))
+            return s;
+        StringBuffer sb = new StringBuffer();
+        for (int i=0; i<s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '\\') {
+                sb.append('\\');
+                sb.append('\\');
+            } else if (c == '$') {
+                sb.append('\\');
+                sb.append('$');
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * Return the ISO code of the language used by the framework
+     *
+     * @return String
+     */
+    public static String getSelectedLanguage() {
+        synchronized (sync) {
+            if(currentClass == null) return null;
+            return currentClass.selectedLanguage;
+        }
+    }
+    
+    /**
+     * Load a translation file depending on the given name and using the prefix
+     *
+     * @param name
+     * @return the Properties object or null if not found
+     */
+    public static SimpleFieldSet loadTranslation(String name) {
+        name = PREFIX.replace('.', 
'/').concat(PREFIX.concat(name.concat(SUFFIX)));
+        
+        SimpleFieldSet result = null;
+        InputStream in = null;
+        try {
+            ClassLoader loader = ClassLoader.getSystemClassLoader();
+            
+            // Returns null on lookup failures:
+            in = loader.getResourceAsStream(name);
+            if(in != null)
+                result = SimpleFieldSet.readFrom(in, false, false);
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Error while loading the l10n file from " 
+ name + " :" + e.getMessage(), e);
+            result = null;
+        } finally {
+            Closer.close(in);
+        }
+        
+        return result;
+    }
+    
+    public static boolean isOverridden(String key) {
+        synchronized(sync) {
+            if(translationOverride == null) return false;
+            return translationOverride.get(key) != null;
+        }
+    }
+    
+    public static String getString(String key, String pattern, String value) {
+        return getString(key, new String[] { pattern }, new String[] { value 
}); // FIXME code efficiently!
+    }
+    
+    public static char getMnemonic(String mnemoKey){
+        String key = mnemoKey + ".mnemonic";
+        String tmp = getString(key);
+        char ret;
+        if (tmp.length() != 0) {
+            ret = tmp.charAt(0);
+        } else {
+            //Should never happen, but did once...
+            System.out.println("Mnemonic key not found : " + key);
+            ret = '_';
+        }
+        return ret;
+    }
+    
+}

Added: trunk/apps/thingamablog/src/thingamablog/l10n/LineReader.java
===================================================================
--- trunk/apps/thingamablog/src/thingamablog/l10n/LineReader.java               
                (rev 0)
+++ trunk/apps/thingamablog/src/thingamablog/l10n/LineReader.java       
2008-04-22 23:39:58 UTC (rev 19512)
@@ -0,0 +1,16 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package thingamablog.l10n;
+
+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/thingamablog/src/thingamablog/l10n/SimpleFieldSet.java
===================================================================
--- trunk/apps/thingamablog/src/thingamablog/l10n/SimpleFieldSet.java           
                (rev 0)
+++ trunk/apps/thingamablog/src/thingamablog/l10n/SimpleFieldSet.java   
2008-04-22 23:39:58 UTC (rev 19512)
@@ -0,0 +1,888 @@
+/*
+ * SimpleFieldSet.java
+ *
+ * Created on 16 avril 2008, 22:23
+ *
+ * To change this template, choose Tools | Template Manager
+ * and open the template in the editor.
+ */
+
+package thingamablog.l10n;
+
+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.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.Map;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import thingamablog.l10n.FSParseException;
+import thingamablog.l10n.LineReader;
+import net.sf.thingamablog.util.io.Closer;
+
+/**
+ * @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;
+    private final boolean shortLived;
+    static public final char MULTI_LEVEL_CHAR = '.';
+    static public final char MULTI_VALUE_CHAR = ';';
+    static public final char KEYVALUE_SEPARATOR_CHAR = '=';
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+    private Logger logger = Logger.getLogger("net.sf.thingamablog.L10n");
+    
+    /**
+     * Create a SimpleFieldSet.
+     * @param shortLived If false, strings will be interned to ensure that 
they use as
+     * little memory as possible. Only set to true if the SFS will be 
short-lived or
+     * small.
+     */
+    public SimpleFieldSet(boolean shortLived) {
+        values = new HashMap();
+        subsets = null;
+        this.shortLived = shortLived;
+    }
+    
+    /**
+     * Construct a SimpleFieldSet from reading a BufferedReader.
+     * @param br
+     * @param allowMultiple If true, multiple lines with the same field name 
will be
+     * combined; if false, the constructor will throw.
+     * @param shortLived If false, strings will be interned to ensure that 
they use as
+     * little memory as possible. Only set to true if the SFS will be 
short-lived or
+     * small.
+     * @throws IOException If the buffer could not be read, or if there was a 
formatting
+     * problem.
+     */
+    public SimpleFieldSet(BufferedReader br, boolean allowMultiple, boolean 
shortLived) throws IOException {
+        this(shortLived);
+        read(br, allowMultiple);
+    }
+    
+    public SimpleFieldSet(SimpleFieldSet sfs){
+        values = new HashMap(sfs.values);
+        if(sfs.subsets != null)
+            subsets = new HashMap(sfs.subsets);
+        this.shortLived = false; // it's been copied!
+        endMarker = sfs.endMarker;
+    }
+    
+    public SimpleFieldSet(LineReader lis, int maxLineLength, int 
lineBufferSize, boolean tolerant, boolean utf8OrIso88591, boolean 
allowMultiple, boolean shortLived) throws IOException {
+        this(shortLived);
+        read(lis, maxLineLength, lineBufferSize, tolerant, utf8OrIso88591, 
allowMultiple);
+    }
+    
+    /**
+     * Construct from a string.
+     * String format:
+     * blah=blah
+     * blah=blah
+     * End
+     * @param shortLived If false, strings will be interned to ensure that 
they use as
+     * little memory as possible. Only set to true if the SFS will be 
short-lived or
+     * small.
+     * @throws IOException if the string is too short or invalid.
+     */
+    public SimpleFieldSet(String content, boolean allowMultiple, boolean 
shortLived) throws IOException {
+        this(shortLived);
+        StringReader sr = new StringReader(content);
+        BufferedReader br = new BufferedReader(sr);
+        read(br, allowMultiple);
+    }
+    
+    /**
+     * Read from disk
+     * Format:
+     * blah=blah
+     * blah=blah
+     * End
+     * @param allowMultiple
+     */
+    private void read(BufferedReader br, boolean allowMultiple) throws 
IOException {
+        boolean firstLine = true;
+        while(true) {
+            String line = br.readLine();
+            if(line == null) {
+                if(firstLine) throw new EOFException();
+                throw new IOException("No end Marker!");
+            }
+            firstLine = false;
+            int index = line.indexOf(KEYVALUE_SEPARATOR_CHAR);
+            if(index >= 0) {
+                // Mapping
+                String before = line.substring(0, index);
+                String after = line.substring(index+1);
+                if(!shortLived) after = after.intern();
+                put(before, after, allowMultiple, false);
+            } 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, boolean allowMultiple) throws IOException {
+        boolean firstLine = true;
+        while(true) {
+            String line = br.readLine(maxLength, bufferSize, utfOrIso88591);
+            if(line == null) {
+                if(firstLine) throw new EOFException();
+                if(tolerant)
+                    logger.log(Level.SEVERE, "No end marker");
+                else
+                    throw new IOException("No end marker");
+                return;
+            }
+            if((line.length() == 0) && tolerant) continue; // ignore
+            firstLine = false;
+            int index = line.indexOf(KEYVALUE_SEPARATOR_CHAR);
+            if(index >= 0) {
+                // Mapping
+                String before = line.substring(0, index);
+                String after = line.substring(index+1);
+                if(!shortLived) after = after.intern();
+                put(before, after, allowMultiple, false);
+            } 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 (subset("") == null) ? null : 
subset("").get(key.substring(1));
+        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) {
+        String k = get(key);
+        if(k == null) return null;
+        return split(k);
+    }
+    
+    private static final String[] split(String string) {
+        if(string == null) return new String[0];
+        return string.split(String.valueOf(MULTI_VALUE_CHAR)); // 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();
+    }
+    
+    private static final String unsplit(String[] strings) {
+        StringBuffer sb = new StringBuffer();
+        for(int i=0;i<strings.length;i++) {
+            if(i != 0) sb.append(MULTI_VALUE_CHAR);
+            sb.append(strings[i]);
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * Put contents of a fieldset, overwrite old values.
+     */
+    public void putAllOverwrite(SimpleFieldSet fs) {
+        Iterator i = fs.values.keySet().iterator();
+        while(i.hasNext()) {
+            String key = (String) i.next();
+            String hisVal = (String) fs.values.get(key);
+            values.put(key, hisVal); // overwrite old
+        }
+        if(fs.subsets == null) return;
+        if(subsets == null) subsets = new HashMap();
+        i = fs.subsets.keySet().iterator();
+        while(i.hasNext()) {
+            String key = (String) i.next();
+            SimpleFieldSet hisFS = (SimpleFieldSet) fs.subsets.get(key);
+            SimpleFieldSet myFS = (SimpleFieldSet) subsets.get(key);
+            if(myFS != null) {
+                myFS.putAllOverwrite(hisFS);
+            } else {
+                subsets.put(key, hisFS);
+            }
+        }
+    }
+    
+    /**
+     * Set a key to a value. If the value already exists, throw 
IllegalStateException.
+     * @param key The key.
+     * @param value The value.
+     */
+    public void putSingle(String key, String value) {
+        if(value == null) return;
+        if(!shortLived) value = value.intern();
+        if(!put(key, value, false, false))
+            throw new IllegalStateException("Value already exists: "+value+" 
but want to set "+key+" to "+value);
+    }
+    
+    /**
+     * Aggregating put. Set a key to a value, if the value already exists, 
append to it.
+     * @param key The key.
+     * @param value The value.
+     */
+    public void putAppend(String key, String value) {
+        if(value == null) return;
+        if(!shortLived) value = value.intern();
+        put(key, value, true, false);
+    }
+    
+    /**
+     * Set a key to a value, overwriting any existing value if present.
+     * @param key The key.
+     * @param value The value.
+     */
+    public void putOverwrite(String key, String value) {
+        if(value == null) return;
+        if(!shortLived) value = value.intern();
+        put(key, value, false, true);
+    }
+    
+    /**
+     * Set a key to a value.
+     * @param key The key.
+     * @param value The value.
+     * @param allowMultiple If true, if the key already exists then the value 
will be
+     * appended to the existing value. If false, we return false to indicate 
that the
+     * old value is unchanged.
+     * @return True unless allowMultiple was false and there was a 
pre-existing value,
+     * or value was null.
+     */
+    private synchronized final boolean put(String key, String value, boolean 
allowMultiple, boolean overwrite) {
+        int idx;
+        if(value == null) return true; // valid no-op
+        if(value.indexOf('\n') != -1) throw new IllegalArgumentException("A 
simplefieldSet can't accept newlines !");
+        if((idx = key.indexOf(MULTI_LEVEL_CHAR)) == -1) {
+            String x = (String) values.get(key);
+            
+            if(!shortLived) key = key.intern();
+            if(x == null || overwrite) {
+                values.put(key, value);
+            } else {
+                if(!allowMultiple) return false;
+                values.put(key, ((String)values.get(key))+ MULTI_VALUE_CHAR 
+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(shortLived);
+                if(!shortLived) before = before.intern();
+                subsets.put(before, fs);
+            }
+            fs.put(after, value, allowMultiple, overwrite);
+        }
+        return true;
+    }
+    
+    public void put(String key, int value) {
+        // Use putSingle so it does the intern check
+        putSingle(key, Integer.toString(value));
+    }
+    
+    public void put(String key, long value) {
+        putSingle(key, Long.toString(value));
+    }
+    
+    public void put(String key, short value) {
+        putSingle(key, Short.toString(value));
+    }
+    
+    public void put(String key, char c) {
+        putSingle(key, Character.toString(c));
+    }
+    
+    public void put(String key, boolean b) {
+        // Don't use putSingle, avoid intern check (Boolean.toString returns 
interned strings anyway)
+        put(key, Boolean.toString(b), false, false);
+    }
+    
+    public void put(String key, double windowSize) {
+        putSingle(key, Double.toString(windowSize));
+    }
+    
+    /**
+     * Write the contents of the SimpleFieldSet to a Writer.
+     * Note: The caller *must* buffer the writer to avoid lousy performance!
+     * (StringWriter is by definition buffered, otherwise wrap it in a 
BufferedWriter)
+     *
+     * @warning keep in mind that a Writer is not necessarily UTF-8!!
+     */
+    public void writeTo(Writer w) throws IOException {
+        writeTo(w, "", false);
+    }
+    
+    /**
+     * Write the contents of the SimpleFieldSet to a Writer.
+     * Note: The caller *must* buffer the writer to avoid lousy performance!
+     * (StringWriter is by definition buffered, otherwise wrap it in a 
BufferedWriter)
+     *
+     * @warning keep in mind that a Writer is not necessarily UTF-8!!
+     */
+    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);
+            w.write(key);
+            w.write(KEYVALUE_SEPARATOR_CHAR);
+            w.write(value);
+            w.write('\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);
+                w.write('\n');
+            }
+        }
+    }
+    
+    public void writeToOrdered(Writer w) throws IOException {
+        writeToOrdered(w, "", false);
+    }
+    
+    private synchronized void writeToOrdered(Writer w, String prefix, boolean 
noEndMarker) throws IOException {
+        String[] keys = (String[]) values.keySet().toArray(new 
String[values.size()]);
+        int i=0;
+        
+        // Sort
+        Arrays.sort(keys);
+        
+        // Output
+        for(i=0; i < keys.length; i++)
+            w.write(prefix+keys[i]+KEYVALUE_SEPARATOR_CHAR+get(keys[i])+'\n');
+        
+        if(subsets != null) {
+            String[] orderedPrefixes = (String[]) subsets.keySet().toArray(new 
String[subsets.size()]);
+            // Sort
+            Arrays.sort(orderedPrefixes);
+            
+            for(i=0; i < orderedPrefixes.length; i++) {
+                SimpleFieldSet subset = subset(orderedPrefixes[i]);
+                if(subset == null) throw new NullPointerException();
+                subset.writeToOrdered(w, 
prefix+orderedPrefixes[i]+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) {
+            logger.log(Level.SEVERE, "WTF?!: "+e+" in toString()!", e);
+        }
+        return sw.toString();
+    }
+    
+    public String toOrderedString() {
+        StringWriter sw = new StringWriter();
+        try {
+            writeToOrdered(sw);
+        } catch (IOException e) {
+            logger.log(Level.SEVERE, "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);
+    }
+    
+    /**
+     * Like subset(), only throws instead of returning null.
+     * @throws FSParseException
+     */
+    public synchronized SimpleFieldSet getSubset(String key) throws 
FSParseException {
+        SimpleFieldSet fs = subset(key);
+        if(fs == null) throw new FSParseException("No such subset "+key);
+        return fs;
+    }
+    
+    public Iterator keyIterator() {
+        return new KeyIterator("");
+    }
+    
+    public KeyIterator keyIterator(String prefix) {
+        return new KeyIterator(prefix);
+    }
+    
+    public Iterator toplevelKeyIterator() {
+        return values.keySet().iterator();
+    }
+    
+    public class KeyIterator implements Iterator {
+        
+        final Iterator valuesIterator;
+        final Iterator subsetIterator;
+        KeyIterator subIterator;
+        String prefix;
+        
+        /**
+         * It provides an iterator for the SimpleSetField
+         * which passes through every key.
+         * (e.g. for key1=value1 key2.sub2=value2 key1.sub=value3
+         * it will provide key1,key2.sub2,key1.sub)
+         * @param a prefix to put BEFORE every key
+         * (e.g. for key1=value, if the iterator is created with prefix 
"aPrefix",
+         * it will provide aPrefixkey1
+         */
+        public KeyIterator(String prefix) {
+            synchronized(SimpleFieldSet.this) {
+                valuesIterator = values.keySet().iterator();
+                if(subsets != null)
+                    subsetIterator = subsets.keySet().iterator();
+                else
+                    subsetIterator = null;
+                while(true) {
+                    if(valuesIterator != null && valuesIterator.hasNext()) 
break;
+                    if(subsetIterator == null || !subsetIterator.hasNext()) 
break;
+                    String name = (String) subsetIterator.next();
+                    if(name == null) continue;
+                    SimpleFieldSet fs = (SimpleFieldSet) subsets.get(name);
+                    if(fs == null) continue;
+                    String newPrefix = prefix + name + MULTI_LEVEL_CHAR;
+                    subIterator = fs.keyIterator(newPrefix);
+                    if(subIterator.hasNext()) break;
+                    subIterator = null;
+                }
+                this.prefix = prefix;
+            }
+        }
+        
+        public boolean hasNext() {
+            synchronized(SimpleFieldSet.this) {
+                while(true) {
+                    if(valuesIterator.hasNext()) return true;
+                    if((subIterator != null) && subIterator.hasNext()) return 
true;
+                    if(subIterator != null) 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);
+                    } else
+                        return false;
+                }
+            }
+        }
+        
+        public final Object next() {
+            return nextKey();
+        }
+        
+        public String nextKey() {
+            synchronized(SimpleFieldSet.this) {
+                String ret = null;
+                if(valuesIterator != null && valuesIterator.hasNext()) {
+                    return prefix + valuesIterator.next();
+                }
+                // Iterate subsets.
+                while(true) {
+                    if(subIterator != null && subIterator.hasNext()) {
+                        // If we have a retval, and we have a next value, 
return
+                        if(ret != null) return ret;
+                        ret = (String) subIterator.next();
+                        if(subIterator.hasNext())
+                            // If we have a retval, and we have a next value, 
return
+                            if(ret != null) return ret;
+                    }
+                    // Otherwise, we need to get a new subIterator (or 
hasNext() will return false)
+                    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);
+                    } else {
+                        // No more subIterator's
+                        if(ret == null)
+                            logger.log(Level.SEVERE, "Returning null from 
KeyIterator.nextKey() - should never happen!");
+                        return ret;
+                    }
+                }
+            }
+        }
+        
+        public synchronized void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+    
+    /** Tolerant put(); does nothing if fs is empty */
+    public void tput(String key, SimpleFieldSet fs) {
+        if(fs == null || fs.isEmpty()) return;
+        put(key, fs);
+    }
+    
+    public void put(String key, SimpleFieldSet fs) {
+        if(fs == null) return; // legal no-op, because used everywhere
+        if(fs.isEmpty()) // can't just no-op, because caller might add the FS 
then populate it...
+            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!");
+        if(!shortLived) key = key.intern();
+        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;
+            }
+        }
+    }
+    
+    /**
+     * It removes the specified subset.
+     * For example, in a SimpleFieldSet like this:
+     * foo=bar
+     * foo.bar=foobar
+     * foo.bar.boo=foobarboo
+     * calling it with the parameter "foo"
+     * means to drop the second and the third line.
+     * @param is the subset to remove
+     */
+    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 == null) ? null : subsets.keySet().iterator();
+    }
+    
+    public String[] namesOfDirectSubsets() {
+        return (subsets == null) ? EMPTY_STRING_ARRAY : (String[]) 
subsets.keySet().toArray(new String[subsets.size()]);
+    }
+    
+    public static SimpleFieldSet readFrom(InputStream is, boolean 
allowMultiple, boolean shortLived) throws IOException {
+        BufferedInputStream bis = null;
+        InputStreamReader isr = null;
+        BufferedReader br = null;
+        
+        try {
+            bis = new BufferedInputStream(is);
+            try {
+                isr = new InputStreamReader(bis, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                System.out.println("Impossible: "+e);
+                e.getStackTrace();
+                is.close();
+                return null;
+            }
+            br = new BufferedReader(isr);
+            SimpleFieldSet fs = new SimpleFieldSet(br, allowMultiple, 
shortLived);
+            br.close();
+            
+            return fs;
+        } finally {
+            Closer.close(br);
+            Closer.close(isr);
+            Closer.close(bis);
+        }
+    }
+    
+    public static SimpleFieldSet readFrom(File f, boolean allowMultiple, 
boolean shortLived) throws IOException {
+        return readFrom(new FileInputStream(f), allowMultiple, shortLived);
+    }
+    
+    /** Write to the given OutputStream, close it and flush it. */
+    public void writeTo(OutputStream os) throws IOException {
+        BufferedOutputStream bos = null;
+        OutputStreamWriter osw = null;
+        BufferedWriter bw = null;
+        
+        try {
+            bos = new BufferedOutputStream(os);
+            try {
+                osw = new OutputStreamWriter(bos, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                logger.log(Level.SEVERE, "Impossible: " + e, e);
+                os.close();
+                return;
+            }
+            bw = new BufferedWriter(osw);
+            writeTo(bw);
+            // close() calls flush() but IGNORES ALL ERRORS!
+            bw.flush();
+            bw.close();
+        }finally {
+            Closer.close(bw);
+            Closer.close(osw);
+            Closer.close(bos);
+        }
+    }
+    
+    public int 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 int getInt(String key) throws FSParseException {
+        String s = get(key);
+        if(s == null) throw new FSParseException("No key "+key);
+        try {
+            return Integer.parseInt(s);
+        } catch (NumberFormatException e) {
+            throw new FSParseException("Cannot parse "+s+" for integer "+key);
+        }
+    }
+    
+    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 double getDouble(String key) throws FSParseException {
+        String s = get(key);
+        if(s == null) throw new FSParseException("No key "+key);
+        try {
+            return Double.parseDouble(s);
+        } catch (NumberFormatException e) {
+            throw new FSParseException("Cannot parse "+s+" for integer "+key);
+        }
+    }
+    
+    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 long getLong(String key) throws FSParseException {
+        String s = get(key);
+        if(s == null) throw new FSParseException("No key "+key);
+        try {
+            return Long.parseLong(s);
+        } catch (NumberFormatException e) {
+            throw new FSParseException("Cannot parse "+s+" for long "+key);
+        }
+    }
+    
+    public short getShort(String key) throws FSParseException {
+        String s = get(key);
+        if(s == null) throw new FSParseException("No key "+key);
+        try {
+            return Short.parseShort(s);
+        } catch (NumberFormatException e) {
+            throw new FSParseException("Cannot parse "+s+" for short "+key);
+        }
+    }
+    
+    public short getShort(String key, short def) {
+        String s = get(key);
+        if(s == null) return def;
+        try {
+            return Short.parseShort(s);
+        } catch (NumberFormatException e) {
+            return def;
+        }
+    }
+    
+    public char getChar(String key) throws FSParseException {
+        String s = get(key);
+        if(s == null) throw new FSParseException("No key "+key);
+        if (s.length() == 1)
+            return s.charAt(0);
+        else
+            throw new FSParseException("Cannot parse "+s+" for char "+key);
+    }
+    
+    public char getChar(String key, char def) {
+        String s = get(key);
+        if(s == null) return def;
+        if (s.length() == 1)
+            return s.charAt(0);
+        else
+            return def;
+    }
+    
+    public boolean getBoolean(String key, boolean def) {
+        return Fields.stringToBool(get(key), def);
+    }
+    
+    public boolean getBoolean(String key) throws FSParseException {
+        try {
+            return Fields.stringToBool(get(key));
+        } catch(NumberFormatException e) {
+            throw new FSParseException(e);
+        }
+    }
+    
+    public void put(String key, int[] value) {
+        // FIXME this could be more efficient...
+        removeValue(key);
+        for(int i=0;i<value.length;i++)
+            putAppend(key, Integer.toString(value[i]));
+    }
+    
+    public int[] getIntArray(String key) {
+        String[] strings = getAll(key);
+        if(strings == null) return null;
+        int[] ret = new int[strings.length];
+        for(int i=0;i<strings.length;i++) {
+            try {
+                ret[i] = Integer.parseInt(strings[i]);
+            } catch (NumberFormatException e) {
+                logger.log(Level.SEVERE, "Cannot parse "+strings[i]+" : "+e, 
e);
+                return null;
+            }
+        }
+        return ret;
+    }
+    
+    public void putOverwrite(String key, String[] strings) {
+        putOverwrite(key, unsplit(strings));
+    }
+    
+    public String getString(String key) throws FSParseException {
+        String s = get(key);
+        if(s == null) throw new FSParseException("No such element "+key);
+        return s;
+    }
+    
+}

Added: trunk/apps/thingamablog/src/thingamablog/l10n/i18n.java
===================================================================
--- trunk/apps/thingamablog/src/thingamablog/l10n/i18n.java                     
        (rev 0)
+++ trunk/apps/thingamablog/src/thingamablog/l10n/i18n.java     2008-04-22 
23:39:58 UTC (rev 19512)
@@ -0,0 +1,49 @@
+/*
+ * i18n.java
+ *
+ * Created on 21 avril 2008, 17:17
+ */
+
+package thingamablog.l10n;
+
+import java.lang.reflect.Method;
+import java.util.Locale;
+import thingamablog.l10n.L10n;
+
+/**
+ * Extends the L10n class, to keep the same name and interface as before
+ * @author dieppe
+ */
+public class i18n extends L10n{
+    
+    i18n(String selected){
+        super(selected);
+    }
+    
+    public static void setLocale(String selected){
+        setLanguage(selected);
+    }
+    
+    public static String str(String key){
+        return getString(key);
+    }
+    
+    public static char mnem(String MnemoKey){
+        return getMnemonic(MnemoKey);
+    }
+    
+    public static Locale getLocale(){
+        String loc = getSelectedLanguage();
+        return new Locale(loc);
+    }
+    
+    public static Locale[] getAvailableLanguagePackLocales() {
+        int Length = AVAILABLE_LANGUAGES.length;
+        Locale result[] = new Locale[Length];
+        for(int i = 0; i<Length; i++) {
+            String loc = AVAILABLE_LANGUAGES[i];
+            result[i]= new Locale(loc);
+        }
+        return result;
+    }
+}


Reply via email to