[jsword-svn] jsword/java/jsword/org/crosswire/jsword/book/sword s

jswordcvs at crosswire.org jswordcvs at crosswire.org
Sun Feb 27 19:21:42 MST 2005


Update of /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword
In directory www.crosswire.org:/tmp/cvs-serv26368/java/jsword/org/crosswire/jsword/book/sword

Modified Files:
	SwordBookDriver.java CompressionType.java SwordConstants.java 
	SwordBookMetaData.java ConfigEntry.java ModuleType.java 
Added Files:
	ConfigEntryTable.java ConfigEntryType.java 
Log Message:
Fixed the display of the conf (BD-16, 17 & 18)

Index: SwordBookMetaData.java
===================================================================
RCS file: /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword/SwordBookMetaData.java,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** SwordBookMetaData.java	25 Jan 2005 00:02:32 -0000	1.35
--- SwordBookMetaData.java	28 Feb 2005 02:21:40 -0000	1.36
***************
*** 1,30 ****
  package org.crosswire.jsword.book.sword;
  
- import java.awt.ComponentOrientation;
- import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileReader;
  import java.io.IOException;
- import java.io.PrintWriter;
  import java.io.Reader;
! import java.net.URL;
! import java.util.ArrayList;
! import java.util.HashSet;
  import java.util.Iterator;
- import java.util.LinkedHashMap;
  import java.util.List;
- import java.util.Locale;
  import java.util.Map;
- import java.util.Set;
- import java.util.TreeMap;
  
- import org.crosswire.common.util.Histogram;
- import org.crosswire.common.util.Logger;
- import org.crosswire.common.util.NetUtil;
- import org.crosswire.common.util.StringUtil;
  import org.crosswire.jsword.book.BookType;
  import org.crosswire.jsword.book.basic.AbstractBookMetaData;
  import org.crosswire.jsword.book.filter.Filter;
  import org.crosswire.jsword.book.filter.FilterFactory;
  
  /**
--- 1,18 ----
  package org.crosswire.jsword.book.sword;
  
  import java.io.File;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.Reader;
! import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import org.crosswire.jsword.book.BookType;
  import org.crosswire.jsword.book.basic.AbstractBookMetaData;
  import org.crosswire.jsword.book.filter.Filter;
  import org.crosswire.jsword.book.filter.FilterFactory;
+ import org.jdom.Document;
  
  /**
***************
*** 84,373 ****
      {
          setBook(null);
-         this.internal = internal;
-         this.supported = true;
- 
-         // read the config file
-         BufferedReader bin = new BufferedReader(in);
-         StringBuffer buf = new StringBuffer();
-         while (true)
-         {
-             // Empty out the buffer
-             buf.setLength(0);
- 
-             String line = advance(bin);
-             if (line == null)
-             {
-                 break;
-             }
- 
-             int length = line.length();
-             if (length > 0)
-             {
-                 buf.append(line);
-             }
-             else
-             {
-                 buf.append(' ');
-             }
- 
-             getContinuation(bin, buf);
- 
-             parseEntry(buf.toString());
-         }
- 
-         // merge entries into properties file
-         for (Iterator kit = getKeys(); kit.hasNext();)
-         {
-             String key = (String) kit.next();
- 
-             // Only copy the valid ones
-             if (ConfigEntry.fromString(key) == null)
-             {
-                 continue;
-             }
- 
-             List list = (List) table.get(key);
- 
-             StringBuffer combined = new StringBuffer();
-             boolean appendSeparator = false;
-             for (Iterator vit = list.iterator(); vit.hasNext();)
-             {
-                 String element = (String) vit.next();
-                 if (appendSeparator)
-                 {
-                     combined.append('\n');
-                 }
-                 combined.append(element);
-                 appendSeparator = true;
-             }
- 
-             putProperty(key, combined.toString());
-         }
- 
-         // set the key property file entries
-         putProperty(KEY_INITIALS, initials);
- 
-         validate();
- 
-         if (!isSupported())
-         {
-             return;
-         }
- 
-         // From the config map, extract the important bean properties
-         String modTypeName = getProperty(ConfigEntry.MOD_DRV);
-         if (modTypeName != null)
-         {
-             mtype = ModuleType.getModuleType(modTypeName);
-         }
  
!         // The default compression type is BOOK.
!         // This probably a data problem as it only occurs with webstersdict
!         String cStr = getProperty(ConfigEntry.COMPRESS_TYPE);
!         String blocking = getProperty(ConfigEntry.BLOCK_TYPE);
!         if (cStr != null && blocking == null)
!         {
!             log.warn("Fixing data for " + internal + ". Adding BlockType of BOOK"); //$NON-NLS-1$ //$NON-NLS-2$
!             fixEntry(ConfigEntry.BLOCK_TYPE, BlockType.BLOCK_BOOK.toString());
!         }
! 
!         String lang = getProperty(ConfigEntry.LANG);
!         putProperty(KEY_LANGUAGE, getLanguage(internal, lang));
!         if (lang != null) //$NON-NLS-1$
!         {
!             // This returns ComponentOrientation.LEFT_TO_RIGHT if
!             // it does not know what it is.
!             boolean leftToRight = ComponentOrientation.getOrientation(new Locale(lang)).isLeftToRight();
!             String dir = getProperty(ConfigEntry.DIRECTION);
!             // Java thinks it is RtoL but it is not stated to be such in the conf.
!             if (!leftToRight && dir == null)
!             {
!                 log.warn("Fixing data for " + internal + ". Adding DIRECTION=RtoL"); //$NON-NLS-1$ //$NON-NLS-2$
!                 fixEntry(ConfigEntry.DIRECTION, SwordConstants.DIRECTION_STRINGS[SwordConstants.DIRECTION_RTOL]);
!             }
!             // Java thinks it is LtoR but it is stated to be something else
!             String ltor = SwordConstants.DIRECTION_STRINGS[SwordConstants.DIRECTION_LTOR];
!             if (leftToRight && dir != null && !dir.equals(ltor))
!             {
!                 log.warn("Fixing data for " + internal + ". Changing DIRECTION=RtoL to LtoR"); //$NON-NLS-1$ //$NON-NLS-2$
!                 fixEntry(ConfigEntry.DIRECTION, ltor);
!             }
!         }
! 
!         if (mtype != null)
!         {
!             BookType type = mtype.getBookType();
!             putProperty(KEY_TYPE, type != null ? type.toString() : ""); //$NON-NLS-1$
!         }
! 
!         // now that prop is fully populated we can organize it.
!         organize();
!     }
! 
!     /**
!      * Save this config file to a URL
!      * @param dest The URL to save the data to
!      */
!     public void save(URL dest)
!     {
!         PrintWriter out = null;
!         try
!         {
!             out = new PrintWriter(NetUtil.getOutputStream(dest));
!             out.print(data);
!         }
!         catch (IOException e)
!         {
!             e.printStackTrace();
!         }
!         finally
!         {
!             if (out != null)
!             {
!                 out.close();
!             }
!         }
!     }
! 
!     /**
!      * Returns an Enumeration of all the keys found in the config file.
!      */
!     public Iterator getKeys()
!     {
!         return table.keySet().iterator();
!     }
! 
!     /**
!      * returns the index of the array element that matches the specified string
!      */
!     private int matchingIndex(String[] array, ConfigEntry title, int deft)
!     {
!         String value = getProperty(title);
!         if (value == null)
!         {
!             return deft;
!         }
! 
!         for (int i = 0; i < array.length; i++)
!         {
!             if (value.equalsIgnoreCase(array[i]))
!             {
!                 return i;
!             }
!         }
! 
!         // Some debug to say: no match
!         log.error("String " + value + " (title=" + title + ") not found in array: " + StringUtil.join(array, ", ")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
!         return deft;
!     }
! 
!     /**
!      * Sort the keys for a more meaningful presentation order.
!      * TODO(DM): Replace this with a conversion of the properties to XML and then by XSLT to HTML.
!      */
!     private void organize()
!     {
!         Map orderedMap = new LinkedHashMap();
!         organize(orderedMap, BASIC_INFO);
!         organize(orderedMap, LANG_INFO);
!         organize(orderedMap, HISTORY_INFO);
!         organize(orderedMap, SYSTEM_INFO);
!         organize(orderedMap, COPYRIGHT_INFO);
!         // add everything else in sorted order
!         orderedMap.putAll(new TreeMap(getProperties()));
!         setProperties(orderedMap);
!     }
! 
!     /**
!      * 
!      */
!     private void organize(Map result, Object[] category)
!     {
!         for (int i = 0; i < category.length; i++)
!         {
!             String key = category[i].toString();
!             Object value = getProperties().remove(key);
!             if (value != null)
!             {
!                 result.put(key, value);
!             }
!         }
!     }
! 
!     /**
!      * 
!      */
!     public void validate()
!     {
!         // See if the ModuleType can handle this BookMetaData
! 
!         // Locked modules are described but don't exist.
!         if (getProperty(ConfigEntry.CIPHER_KEY) != null)
!         {
!             // Don't need to say we don't support locked modules. We already know that!
!             //log.debug("Book not supported: " + internal + " because it is locked."); //$NON-NLS-1$ //$NON-NLS-2$
!             supported = false;
!             return;
!         }
! 
!         // It has to have a usable name
!         String name = getName();
! 
!         if (name == null || name.length() == 0)
!         {
!             log.debug("Book not supported: " + internal + " because it has no name."); //$NON-NLS-1$ //$NON-NLS-2$
!             supported = false;
!             return;
!         }
! 
!         String modTypeName = getProperty(ConfigEntry.MOD_DRV);
!         if (modTypeName == null || modTypeName.length() == 0)
!         {
!             log.error("Book not supported: " + internal + " because it has no " + ConfigEntry.MOD_DRV); //$NON-NLS-1$ //$NON-NLS-2$
!             supported = false;
!             return;
!         }
! 
!         ModuleType type = ModuleType.getModuleType(modTypeName);
!         if (type == null)
!         {
!             log.debug("Book not supported: " + internal + " because no ModuleType for " + modTypeName); //$NON-NLS-1$ //$NON-NLS-2$
!             supported = false;
!             return;
!         }
! 
!         if (type.getBookType() == null)
!         {
!             // We plan to add RawGenBook at a later time. So we don't need to be reminded all the time.
!             if (!modTypeName.equals("RawGenBook")) //$NON-NLS-1$
!             {
!                 log.debug("Book not supported: " + internal + " because missing book type for ModuleType (" + modTypeName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
!             }
!             supported = false;
!             return;
!         }
! 
!         if (!type.isSupported(this))
!         {
!             log.debug("Book not supported: " + internal + " because ModuleType (" + modTypeName + ") is not supported."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
!             supported = false;
!             return;
!         }
! 
!         // report on unknown config entries
!         for (Iterator kit = getKeys(); kit.hasNext();)
          {
!             String key = (String) kit.next();
!             if (ConfigEntry.fromString(key) == null)
!             {
!                 log.warn("Unknown config entry for " + internal + ": " + key); //$NON-NLS-1$ //$NON-NLS-2$
!             }
          }
  
-         // output collected warnings
-         for (Iterator kit = warnings.iterator(); kit.hasNext();)
-         {
-             log.warn((String) kit.next());
-         }
      }
  
--- 72,91 ----
      {
          setBook(null);
  
!         cet = new ConfigEntryTable(in, internal);
! //        Element ele = cet.toOSIS();
! //        SAXEventProvider sep = new JDOMSAXEventProvider(new Document(ele));
! //        try
! //        {
! //        System.out.println(XMLUtil.writeToString(sep));
! //        }
! //        catch(Exception e)
! //        {
! //        }
!         if (isSupported())
          {
!             buildProperties();
          }
  
      }
  
***************
*** 377,381 ****
      public boolean isSupported()
      {
!         return supported;
      }
  
--- 95,99 ----
      public boolean isSupported()
      {
!         return cet.isSupported();
      }
  
***************
*** 385,389 ****
      public String getName()
      {
!         return getProperty(ConfigEntry.DESCRIPTION);
      }
  
--- 103,107 ----
      public String getName()
      {
!         return getProperty(ConfigEntryType.DESCRIPTION);
      }
  
***************
*** 394,405 ****
      public String getModuleCharset()
      {
!         int encoding = matchingIndex(SwordConstants.ENCODING_STRINGS, ConfigEntry.ENCODING, SwordConstants.ENCODING_LATIN1);
! 
!         // There was code here that said:
!         // if (encoding < 0)
!         //  encoding = SwordConstants.ENCODING_UTF8;
!         // However this would never trigger since we give a default above
! 
!         return SwordConstants.ENCODING_JAVA[encoding];
      }
  
--- 112,116 ----
      public String getModuleCharset()
      {
!         return (String) ENCODING_JAVA.get(getProperty(ConfigEntryType.ENCODING));
      }
  
***************
*** 409,413 ****
      public ModuleType getModuleType()
      {
!         return mtype;
      }
  
--- 120,124 ----
      public ModuleType getModuleType()
      {
!         return cet.getModuleType();
      }
  
***************
*** 417,654 ****
      public Filter getFilter()
      {
!         String sourcetype = getProperty(ConfigEntry.SOURCE_TYPE);
          return FilterFactory.getFilter(sourcetype);
      }
  
      /**
-      * Parse a single line.
-      * The About field may use RTF, where \par is a line break.
-      */
-     private void parseEntry(String line)
-     {
-         String key = null;
-         String value = ""; //$NON-NLS-1$
-         int eqpos = line.indexOf('=');
-         if (eqpos == -1)
-         {
-             key = line;
-         }
-         else
-         {
-             key = line.substring(0, eqpos).trim();
-             value = line.substring(eqpos + 1).trim();
-         }
- 
-         if (key.charAt(0) == '[' && key.charAt(key.length() - 1) == ']')
-         {
-             // The conf file contains a leading line of the form [KJV]
-             // This is the acronymn by which Sword refers to it.
-             initials = key.substring(1, key.length() - 1);
-         }
-         else
-         {
-             value = handleRTF(key, value);
-             addEntry(key, value);
-         }
- 
-     }
- 
-     private String handleRTF(String key, String value)
-     {
-         String copy = value;
-         // This method is a hack! It could be made much nicer.
- 
-         // strip \pard
-         copy = copy.replaceAll("\\\\pard ?", ""); //$NON-NLS-1$ //$NON-NLS-2$
- 
-         // replace rtf newlines
-         copy = copy.replaceAll("\\\\pa[er] ?", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
- 
-         // strip whatever \qc is.
-         copy = copy.replaceAll("\\\\qc ?", ""); //$NON-NLS-1$ //$NON-NLS-2$
- 
-         // strip bold and italic
-         copy = copy.replaceAll("\\\\[bi]0? ?", ""); //$NON-NLS-1$ //$NON-NLS-2$
- 
-         // strip unicode characters
-         copy = copy.replaceAll("\\\\u-?[0-9]{4,6}+\\?", ""); //$NON-NLS-1$ //$NON-NLS-2$
- 
-         // strip { and } which are found in {\i text }
-         copy = copy.replaceAll("[{}]", ""); //$NON-NLS-1$ //$NON-NLS-2$
- 
-         if (!allowsRTF.contains(key))
-         {
-             if (!copy.equals(value))
-             {
-                 warnings.add("Ignoring unexpected RTF for " + key + " in " + internal + ": " + value); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-             }
-             return value;
-         }
- 
-         return copy;
-     }
- 
-     /**
-      * Get continuation lines, if any.
-      */
-     private void getContinuation(BufferedReader bin, StringBuffer buf) throws IOException
-     {
-         String key = null;
-         int eqpos = buf.indexOf("="); //$NON-NLS-1$
-         if (eqpos != -1)
-         {
-             key = buf.substring(0, eqpos).trim();
-         }
- 
-         for (String line = advance(bin); line != null; line = advance(bin))
-         {
-             int length = buf.length();
- 
-             // Look for bad data as this condition did exist
-             boolean continuation_expected = buf.charAt(length - 1) == '\\';
- 
-             if (continuation_expected)
-             {
-                 // delete the continuation character
-                 buf.deleteCharAt(length - 1);
-             }
- 
-             if (isKeyLine(line))
-             {
-                 if (continuation_expected)
-                 {
-                     warnings.add("Continuation followed by key for " + key + " in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-                 }
- 
-                 backup(line);
-                 break;
-             }
-             else if (!continuation_expected)
-             {
-                 warnings.add("Line without previous continuation for " + key + " in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-             }
- 
-             if (singleLine.contains(key))
-             {
-                 warnings.add("Ignoring unexpected additional line for " + key + " in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$                
-             }
-             else
-             {
-                 buf.append(line);
-             }
-         }
-     }
- 
-     /**
-      * Get the next line from the input
-      * @param bin The reader to get data from
-      * @return the next line
-      * @throws IOException
-      */
-     private String advance(BufferedReader bin) throws IOException
-     {
-         // Was something put back? If so, return it.
-         if (readahead != null)
-         {
-             String line = readahead;
-             readahead = null;
-             return line;
-         }
- 
-         // Get the next non-blank, non-comment line
-         for (String line = bin.readLine(); line != null; line = bin.readLine())
-         {
-             // Save the original for diagnostics and for save
-             data.append(line).append('\n');
- 
-             // Remove trailing whitespace
-             line = line.trim();
- 
-             int length = line.length();
- 
-             // skip blank and comment lines
-             if (length != 0 && line.charAt(0) != '#')
-             {
-                 return line;
-             }
-         }
-         return null;
-     }
- 
-     /**
-      * Read too far ahead and need to return a line.
-      */
-     private void backup(String oops)
-     {
-         if (oops.length() > 0)
-         {
-             readahead = oops;
-         }
-         else
-         {
-             // should never happen
-             log.error("Backup an empty string for " + internal); //$NON-NLS-1$
-         }
-     }
- 
-     /**
-      * Does this line of text represent a key/value pair?
-      */
-     private boolean isKeyLine(String line)
-     {
-         return line.indexOf('=') != -1;
-     }
- 
-     /**
-      * Add a value to the list of values for a key
-      */
-     private void addEntry(String key, String value)
-     {
-         // check to see if there already values for this key...
-         List list = (ArrayList) table.get(key);
-         if (list != null)
-         {
-             if (multipleEntry.contains(key))
-             {
-                 list.add(value);
-             }
-             else
-             {
-                 warnings.add("Ignoring unexpected additional entry for " + key + " in " + internal + ": " + value); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$                
-             }
-         }
-         else
-         {
-             list = new ArrayList();
-             list.add(value);
-             table.put(key, list);
-         }
-     }
- 
-     /**
-      * Fix the configuration
-      */
-     private void fixEntry(ConfigEntry entry, String value)
-     {
-         String key = entry.toString();
- 
-         table.remove(key);
-         List list = new ArrayList();
-         list.add(value);
-         table.put(key, list);
- 
-         putProperty(key, value);
-     }
- 
-     /**
       * @return Returns the name of this module as it is used for directory and filenames.
       */
      public String getDiskName()
      {
!         return initials.toLowerCase();
      }
  
-     //==========================================================================
- 
      /* (non-Javadoc)
       * @see org.crosswire.jsword.book.BookMetaData#getType()
--- 128,143 ----
      public Filter getFilter()
      {
!         String sourcetype = getProperty(ConfigEntryType.SOURCE_TYPE);
          return FilterFactory.getFilter(sourcetype);
      }
  
      /**
       * @return Returns the name of this module as it is used for directory and filenames.
       */
      public String getDiskName()
      {
!         return getInitials().toLowerCase();
      }
  
      /* (non-Javadoc)
       * @see org.crosswire.jsword.book.BookMetaData#getType()
***************
*** 656,660 ****
      public BookType getType()
      {
!         return mtype.getBookType();
      }
  
--- 145,154 ----
      public BookType getType()
      {
!         return getModuleType().getBookType();
!     }
!     
!     public Document toOSIS()
!     {
!         return new Document(cet.toOSIS());
      }
  
***************
*** 664,668 ****
      public String getInitials()
      {
!         return initials;
      }
  
--- 158,162 ----
      public String getInitials()
      {
!         return getProperty(ConfigEntryType.INITIALS);
      }
  
***************
*** 670,700 ****
       * Get the string value for the property or null if it is not defined.
       * It is assumed that all properties gotten with this method are single line.
!      * @param entry typically a string or a ConfigEntry
       * @return the property or null
       */
!     public String getProperty(Object entry)
      {
!         String result = (String) getProperty(entry.toString());
! 
!         if (result == null)
!         {
!             return null;
!         }
! 
!         int index = result.indexOf('\n');
!         if (index != -1)
!         {
!             log.warn("Fixing data for " + internal + ". Stripping all but first line of " + entry); //$NON-NLS-1$ //$NON-NLS-2$
!             result = result.substring(0, index);
!             if (entry instanceof ConfigEntry)
!             {
!                 fixEntry((ConfigEntry) entry, result);
!             }
!             else
!             {
!                 putProperty(entry.toString(), result);
!             }
!         }
!         return result;
      }
  
--- 164,175 ----
       * Get the string value for the property or null if it is not defined.
       * It is assumed that all properties gotten with this method are single line.
!      * @param entry the ConfigEntryType
       * @return the property or null
       */
!     public String getProperty(ConfigEntryType entry)
      {
!         Object obj = cet.getValue(entry);
!         
!         return obj != null ? obj.toString() : null; 
      }
  
***************
*** 704,848 ****
      public boolean isLeftToRight()
      {
!         String ltor = getProperty(ConfigEntry.DIRECTION);
!         return ltor == null || ltor.equals(SwordConstants.DIRECTION_STRINGS[SwordConstants.DIRECTION_LTOR]);
      }
  
!     public static void dumpStatistics()
      {
!         System.out.println(histogram.toString());
      }
! 
      /**
!      * The log stream
       */
!     private static final Logger log = Logger.getLogger(SwordBookMetaData.class);
! 
!     private static final Object[] BASIC_INFO =
!     {
!         KEY_INITIALS,
!         ConfigEntry.DESCRIPTION,
!         KEY_TYPE,
!         ConfigEntry.LCSH,
!         ConfigEntry.CATEGORY,
!         ConfigEntry.VERSION,
!         ConfigEntry.SWORD_VERSION_DATE,
!     };
! 
!     private static final Object[] LANG_INFO =
!     {
!         KEY_LANGUAGE,
!         ConfigEntry.LANG,
!         ConfigEntry.GLOSSARY_FROM,
!         ConfigEntry.GLOSSARY_TO,
!         ConfigEntry.LEXICON_FROM,
!         ConfigEntry.LEXICON_TO,
!     };
! 
!     private static final Object[] HISTORY_INFO =
!     {
!         ConfigEntry.HISTORY_2_5,
!         ConfigEntry.HISTORY_2_2,
!         ConfigEntry.HISTORY_2_1,
!         ConfigEntry.HISTORY_2_0,
!         ConfigEntry.HISTORY_1_9,
!         ConfigEntry.HISTORY_1_8,
!         ConfigEntry.HISTORY_1_7,
!         ConfigEntry.HISTORY_1_6,
!         ConfigEntry.HISTORY_1_5,
!         ConfigEntry.HISTORY_1_4,
!         ConfigEntry.HISTORY_1_3,
!         ConfigEntry.HISTORY_1_2,
!         ConfigEntry.HISTORY_1_1,
!         ConfigEntry.HISTORY_1_0,
!         ConfigEntry.HISTORY_0_92,
!         ConfigEntry.HISTORY_0_91,
!         ConfigEntry.HISTORY_0_9,
!         ConfigEntry.HISTORY_0_3,
!         ConfigEntry.HISTORY_0_2,
!         ConfigEntry.HISTORY_0_1
!     };
! 
!     private static final Object[] SYSTEM_INFO =
!     {
!         ConfigEntry.DATA_PATH,
!         ConfigEntry.MOD_DRV,
!         ConfigEntry.SOURCE_TYPE,
!         ConfigEntry.BLOCK_TYPE,
!         ConfigEntry.COMPRESS_TYPE,
!         ConfigEntry.ENCODING,
!         ConfigEntry.MINIMUM_VERSION,
!         ConfigEntry.MINIMUM_SWORD_VERSION,
!         ConfigEntry.DIRECTION
!     };
! 
!     private static final Object[] COPYRIGHT_INFO =
!     {
!         ConfigEntry.ABOUT,
!         ConfigEntry.DISTRIBUTION,
!         ConfigEntry.DISTRIBUTION_LICENSE,
!         ConfigEntry.DISTRIBUTION_NOTES,
!         ConfigEntry.DISTRIBUTION_SOURCE,
!         ConfigEntry.COPYRIGHT,
!         ConfigEntry.COPYRIGHT_DATE,
!         ConfigEntry.COPYRIGHT_HOLDER,
!         ConfigEntry.COPYRIGHT_CONTACT_NAME,
!         ConfigEntry.COPYRIGHT_CONTACT_ADDRESS,
!         ConfigEntry.COPYRIGHT_CONTACT_EMAIL,
!         ConfigEntry.COPYRIGHT_NOTES,
!         ConfigEntry.TEXT_SOURCE,
!     };
! 
!     // Some config entries are expected to be single line entries
!     private static final Set singleLine = new HashSet();
!     static
!     {
!         singleLine.add(ConfigEntry.BLOCK_TYPE.toString());
!         singleLine.add(ConfigEntry.DATA_PATH.toString());
!         singleLine.add(ConfigEntry.COMPRESS_TYPE.toString());
!         singleLine.add(ConfigEntry.SOURCE_TYPE.toString());
!         singleLine.add(ConfigEntry.DESCRIPTION.toString());
!         singleLine.add(ConfigEntry.LANG.toString());
!         singleLine.add(ConfigEntry.DIRECTION.toString());
!         singleLine.add(ConfigEntry.CIPHER_KEY.toString());
!         singleLine.add(ConfigEntry.MOD_DRV.toString());
!         singleLine.add(ConfigEntry.ENCODING.toString());
!     }
! 
!     // Some config entries may exist more than once
!     private static final Set multipleEntry = new HashSet();
!     static
!     {
!         multipleEntry.add(ConfigEntry.FEATURE.toString());
!         multipleEntry.add(ConfigEntry.GLOBAL_OPTION_FILTER.toString());
!         multipleEntry.add(ConfigEntry.OBSOLETES.toString());
!     }
! 
!     // Some config entries allow RTF
!     private static final Set allowsRTF = new HashSet();
      static
      {
!         allowsRTF.add(ConfigEntry.ABOUT.toString());
!         allowsRTF.add(ConfigEntry.COPYRIGHT_CONTACT_ADDRESS.toString());
!         allowsRTF.add(ConfigEntry.COPYRIGHT_NOTES.toString());
!         allowsRTF.add(ConfigEntry.COPYRIGHT_CONTACT_NAME.toString());
      }
  
!     /**
!      * A map of lists of known keys. Keys are presented in insertion order
!      */
!     private Map table = new LinkedHashMap();
! 
!     /**
!      * The original name of this config file from mods.d.
!      * This is only used for reporting
!      */
!     private String internal;
  
-     private ModuleType mtype;
-     private String initials = ""; //$NON-NLS-1$
-     private boolean supported;
-     private StringBuffer data = new StringBuffer();
-     private String readahead;
-     private static Histogram histogram = new Histogram();
-     private List warnings = new ArrayList();
  }
--- 179,233 ----
      public boolean isLeftToRight()
      {
!         String dir = getProperty(ConfigEntryType.DIRECTION);
!         return dir == null || dir.equals(ConfigEntryType.DIRECTION.getDefault());
      }
  
!     private void buildProperties()
      {
!         // merge entries into properties file
!         for (Iterator kit = cet.getKeys(); kit.hasNext();)
!         {
!             ConfigEntryType key = (ConfigEntryType) kit.next();
!     
!             Object value = cet.getValue(key);
!             // value is null if the config entry was rejected.
!             if (value == null)
!             {
!                 continue;
!             }
!             if (value instanceof List)
!             {
!                 List list = (List) value;
!                 StringBuffer combined = new StringBuffer();
!                 boolean appendSeparator = false;
!                 for (Iterator vit = list.iterator(); vit.hasNext();)
!                 {
!                     String element = (String) vit.next();
!                     if (appendSeparator)
!                     {
!                         combined.append('\n');
!                     }
!                     combined.append(element);
!                     appendSeparator = true;
!                 }
!     
!                 value = combined.toString();
!             }
!     
!             putProperty(key.toString(), value.toString());
!         }
      }
!  
      /**
!      * The language strings need to be converted to Java charsets
       */
!     static final Map ENCODING_JAVA = new HashMap();
      static
      {
!         ENCODING_JAVA.put("Latin-1", "ISO-8859-1"); //$NON-NLS-1$ //$NON-NLS-2$
!         ENCODING_JAVA.put("UTF-8","UTF-8"); //$NON-NLS-1$ //$NON-NLS-2$
      }
  
!     private ConfigEntryTable cet;
  
  }

Index: ModuleType.java
===================================================================
RCS file: /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword/ModuleType.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** ModuleType.java	26 Jan 2005 00:57:33 -0000	1.12
--- ModuleType.java	28 Feb 2005 02:21:40 -0000	1.13
***************
*** 317,321 ****
      public Book createBook(SwordBookMetaData sbmd, File progdir) throws BookException
      {
!         String dataPath = sbmd.getProperty(ConfigEntry.DATA_PATH);
          File baseurl = new File(progdir, dataPath);
          String path = baseurl.getAbsolutePath();
--- 317,321 ----
      public Book createBook(SwordBookMetaData sbmd, File progdir) throws BookException
      {
!         String dataPath = sbmd.getProperty(ConfigEntryType.DATA_PATH);
          File baseurl = new File(progdir, dataPath);
          String path = baseurl.getAbsolutePath();
***************
*** 341,345 ****
      protected static Backend getCompressedBackend(SwordBookMetaData sbmd, String path) throws BookException
      {
!         String cStr = sbmd.getProperty(ConfigEntry.COMPRESS_TYPE);
          if (cStr != null)
          {
--- 341,345 ----
      protected static Backend getCompressedBackend(SwordBookMetaData sbmd, String path) throws BookException
      {
!         String cStr = sbmd.getProperty(ConfigEntryType.COMPRESS_TYPE);
          if (cStr != null)
          {
***************
*** 354,358 ****
      protected static boolean isCompressedBackendSupported(SwordBookMetaData sbmd)
      {
!         String cStr = sbmd.getProperty(ConfigEntry.COMPRESS_TYPE);
          if (cStr != null)
          {
--- 354,358 ----
      protected static boolean isCompressedBackendSupported(SwordBookMetaData sbmd)
      {
!         String cStr = sbmd.getProperty(ConfigEntryType.COMPRESS_TYPE);
          if (cStr != null)
          {

--- NEW FILE: ConfigEntryTable.java ---
package org.crosswire.jsword.book.sword;

import java.awt.ComponentOrientation;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import org.crosswire.common.util.Logger;
import org.crosswire.jsword.book.BookType;
import org.crosswire.jsword.book.OSISUtil;
import org.crosswire.jsword.book.basic.AbstractBookMetaData;
import org.jdom.Element;

/**
 * A utility class for loading the entries in a Sword module's conf file.
 * Since the conf files are manually maintained, there can be all sorts
 * of errors in them. This class does robust checking and reporting.
 *
 * <p>Config file format. See also:
 * <a href="http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout">
 * http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout</a>
 *
 * <p> The contents of the About field are in rtf.
 * <p> \ is used as a continuation line.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Mark Goodwin [mark at thorubio dot org]
 * @author Joe Walker [joe at eireneh dot com]
 * @author Jacky Cheung
 * @author DM Smith [dmsmith555 at yahoo dot com]
 * @version $Id: ConfigEntryTable.java,v 1.1 2005/02/28 02:21:40 dmsmith Exp $
 */
public class ConfigEntryTable
{
    /**
     * Loads a sword config from a given Reader.
     * @throws IOException
     */
    public ConfigEntryTable(Reader in, String moduleName) throws IOException
    {
        internal = moduleName;
        supported = true;

        loadFile(in);
        adjustLanguage();
        adjustBookType();
        adjustName();
        validate();
    }

    /**
     * Determines whether the Sword Module's conf is supported by JSword.
     */
    public boolean isSupported()
    {
        return supported;
    }
    /**
     * Returns an Enumeration of all the keys found in the config file.
     */
    public Iterator getKeys()
    {
        return table.keySet().iterator();
    }

    /**
     * Returns an Enumeration of all the keys found in the config file.
     */
    public ModuleType getModuleType()
    {
        return moduleType;
    }

    /**
     * Gets a particular ConfigEntry's value by its type
     * @param type of the ConfigEntry
     * @return the requested value, the default (if there is no entry) or null (if there is no default)
     */
    public Object getValue(ConfigEntryType type)
    {
        ConfigEntry ce = (ConfigEntry) table.get(type);
        if (ce != null)
        {
            return ce.getValue();
        }
        return type.getDefault();
    }

    /**
     * Sort the keys for a more meaningful presentation order.
     * TODO(DM): Replace this with a conversion of the properties to XML and then by XSLT to HTML.
     */
    public Element toOSIS()
    {
        OSISUtil.ObjectFactory factory = OSISUtil.factory();
        Element ele = factory.createTable();
        toOSIS(factory, ele, "BasicInfo", BASIC_INFO); //$NON-NLS-1$
        toOSIS(factory, ele, "LangInfo", LANG_INFO); //$NON-NLS-1$
        toOSIS(factory, ele, "LicenseInfo", COPYRIGHT_INFO); //$NON-NLS-1$
        toOSIS(factory, ele, "FeatureInfo", FEATURE_INFO); //$NON-NLS-1$
        toOSIS(factory, ele, "SysInfo", SYSTEM_INFO); //$NON-NLS-1$
        return ele;
    }

    private void loadFile(Reader in) throws IOException
    {
        // read the config file
        BufferedReader bin = new BufferedReader(in);
        loadInitials(bin);
        loadContents(bin);
    }

    private void loadContents(BufferedReader in) throws IOException
    {
        StringBuffer buf = new StringBuffer();
        while (true)
        {
            // Empty out the buffer
            buf.setLength(0);

            String line = advance(in);
            if (line == null)
            {
                break;
            }

            // skip blank lines
            if (line.length() == 0)
            {
                continue;
            }

            int eqpos = line.indexOf("="); //$NON-NLS-1$
            if (eqpos == -1)
            {
                log.warn("Expected to see '=' in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$
                continue;
            }
            
            String key = line.substring(0, eqpos).trim();
            String value = line.substring(eqpos + 1).trim();
            if (value.length() == 0)
            {
                log.warn("Ignoring empty entry in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$
                continue;
            }

            // Create a configEntry so that the name is normalized.
            ConfigEntry configEntry = new ConfigEntry(internal, key);
            
            ConfigEntry e = (ConfigEntry) table.get(configEntry.getType());

            if (e == null)
            {
                ConfigEntryType type = configEntry.getType();
                if (type == null)
                {
                    log.warn("Ignoring unexpected entry in " + internal  + " of " + configEntry.getName()); //$NON-NLS-1$ //$NON-NLS-2$
                }
                else if (type.isSynthetic())
                {
                    log.warn("Ignoring unexpected entry in " + internal  + " of " + configEntry.getName()); //$NON-NLS-1$ //$NON-NLS-2$
                }
                else
                {
                    table.put(configEntry.getType(), configEntry);
                }
            }
            else
            {
                configEntry = e;
            }

            buf.append(value);
            getContinuation(configEntry, in, buf);

            // History is a special case it is of the form History_x.x
            // The config entry is History without the x.x.
            // We want to put x.x at the beginning of the string
            value = buf.toString();
            if (ConfigEntryType.HISTORY.equals(configEntry.getType()))
            {
                int pos = key.indexOf('_');
                value = key.substring(pos + 1) + ' ' + value;
            }

            configEntry.addValue(value);
        }
    }

    private void loadInitials(BufferedReader in) throws IOException
    {
        String initials = null;
        while (true)
        {
            String line = advance(in);
            if (line == null)
            {
                break;
            }

            if (line.charAt(0) == '[' && line.charAt(line.length() - 1) == ']')
            {
                // The conf file contains a leading line of the form [KJV]
                // This is the acronymn by which Sword refers to it.
                initials = line.substring(1, line.length() - 1);
                break;
            }
        }
        if (initials == null)
        {
            log.error("Malformed conf file for " + internal + " no initials found. Using internal of " + internal); //$NON-NLS-1$ //$NON-NLS-2$
            initials = internal;
        }
        add(ConfigEntryType.INITIALS, initials);
    }

    /**
     * Get continuation lines, if any.
     */
    private void getContinuation(ConfigEntry configEntry, BufferedReader bin, StringBuffer buf) throws IOException
    {
        for (String line = advance(bin); line != null; line = advance(bin))
        {
            int length = buf.length();

            // Look for bad data as this condition did exist
            boolean continuation_expected = length > 0 && buf.charAt(length - 1) == '\\';

            if (continuation_expected)
            {
                // delete the continuation character
                buf.deleteCharAt(length - 1);
            }

            if (isKeyLine(line))
            {
                if (continuation_expected)
                {
                    log.warn("Continuation followed by key for " + configEntry.getName() + " in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                }

                backup(line);
                break;
            }
            else if (!continuation_expected)
            {
                log.warn("Line without previous continuation for " + configEntry.getName()  + " in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }

            if (!configEntry.allowsContinuation())
            {
                log.warn("Ignoring unexpected additional line for " + configEntry.getName()  + " in " + internal + ": " + line); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$                
            }
            else
            {
                buf.append(line);
            }
        }
    }

    /**
     * Get the next line from the input
     * @param bin The reader to get data from
     * @return the next line
     * @throws IOException
     */
    private String advance(BufferedReader bin) throws IOException
    {
        // Was something put back? If so, return it.
        if (readahead != null)
        {
            String line = readahead;
            readahead = null;
            return line;
        }

        // Get the next non-blank, non-comment line
        for (String line = bin.readLine(); line != null; line = bin.readLine())
        {
            // Remove trailing whitespace
            line = line.trim();

            int length = line.length();

            // skip blank and comment lines
            if (length != 0 && line.charAt(0) != '#')
            {
                return line;
            }
        }
        return null;
    }

    /**
     * Read too far ahead and need to return a line.
     */
    private void backup(String oops)
    {
        if (oops.length() > 0)
        {
            readahead = oops;
        }
        else
        {
            // should never happen
            log.error("Backup an empty string for " + internal); //$NON-NLS-1$
        }
    }

    /**
     * Does this line of text represent a key/value pair?
     */
    private boolean isKeyLine(String line)
    {
        return line.indexOf('=') != -1;
    }

    /**
     * A helper to create/replace a value for a given type.
     * @param type
     * @param aValue
     */
    private void add(ConfigEntryType type, String aValue)
    {
        table.put(type, new ConfigEntry(internal, type, aValue));
    }

    private void adjustLanguage()
    {
        // Java thinks it is LtoR but it is stated to be something else
        String dir = (String) getValue(ConfigEntryType.DIRECTION);
        String newDir = dir == null ? (String) ConfigEntryType.DIRECTION.getDefault() : dir;

        String langEntry = (String) getValue(ConfigEntryType.LANG);
        String lang = AbstractBookMetaData.getLanguage(internal, langEntry);
        add(ConfigEntryType.LANGUAGE, lang);

        // This returns ComponentOrientation.LEFT_TO_RIGHT if
        // it does not know what it is.
        boolean leftToRight = true;
        if (langEntry != null)
        {
            leftToRight = ComponentOrientation.getOrientation(new Locale(langEntry)).isLeftToRight();
        }

        String langFromEntry = (String) getValue(ConfigEntryType.GLOSSARY_FROM);
        String langToEntry = (String) getValue(ConfigEntryType.GLOSSARY_TO);
        
        if (langFromEntry != null || langToEntry != null)
        {
            String langFrom = AbstractBookMetaData.getLanguage(internal, langFromEntry);
            add(ConfigEntryType.LANGUAGE_FROM, langFrom);
            String langTo = AbstractBookMetaData.getLanguage(internal, langToEntry);
            add(ConfigEntryType.LANGUAGE_TO, langTo);
            boolean fromLeftToRight = true;
            boolean toLeftToRight = true;
            
            if (langFromEntry == null)
            {
                log.warn("Missing data for " + internal + ". Assuming " + ConfigEntryType.GLOSSARY_FROM.getName() + "=" + AbstractBookMetaData.DEFAULT_LANG_CODE);  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
            else
            {
                fromLeftToRight = ComponentOrientation.getOrientation(new Locale(langFromEntry)).isLeftToRight();
            }

            if (langToEntry == null)
            {
                log.warn("Missing data for " + internal + ". Assuming " + ConfigEntryType.GLOSSARY_TO.getName() + "=" + AbstractBookMetaData.DEFAULT_LANG_CODE);  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
            else
            {
                toLeftToRight = ComponentOrientation.getOrientation(new Locale(langToEntry)).isLeftToRight();
            }
            
            // At least one of the two languages should match the lang entry
            if (!langFrom.equals(lang) && !langTo.equals(lang))
            {
                log.error("Data error in " + internal + ". Neither " + ConfigEntryType.GLOSSARY_FROM.getName() + " or " + ConfigEntryType.GLOSSARY_FROM.getName() + " match " + ConfigEntryType.LANG.getName());  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
            }
            
            if (fromLeftToRight != toLeftToRight)
            {
                newDir = ConfigEntryType.DIRECTION_BIDI;
            }
            else if (fromLeftToRight)
            {
                newDir = ConfigEntryType.DIRECTION_LTOR;
            }
            else
            {
                newDir = ConfigEntryType.DIRECTION_RTOL;
            }
        }
        else
        {
            if (leftToRight)
            {
                newDir = ConfigEntryType.DIRECTION_LTOR;
            }
            else
            {
                newDir = ConfigEntryType.DIRECTION_RTOL;
            }
        }

        if (newDir.equals(ConfigEntryType.DIRECTION_LTOR))
        {
            if (dir != null)
            {
                if (!newDir.equals(dir))
                {
                    log.warn("Fixing data for " + internal + ". Changing " + ConfigEntryType.DIRECTION.getName() + "=" + dir + " to " + newDir); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
                }
                table.remove(ConfigEntryType.DIRECTION);
            }
        }
        else if (!newDir.equals(dir))
        {
            if (dir == null)
            {
                log.warn("Fixing data for " + internal + ". Adding " + ConfigEntryType.DIRECTION.getName() + "=" + newDir); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$                
            }
            else
            {
                log.warn("Fixing data for " + internal + ". Changing " + ConfigEntryType.DIRECTION.getName() + "=" + dir + " to " + newDir); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$                   
            }
            add(ConfigEntryType.DIRECTION, newDir);
        }
    }

    private void adjustBookType()
    {
        // From the config map, extract the important bean properties
        String modTypeName = (String) getValue(ConfigEntryType.MOD_DRV);
        if (modTypeName == null)
        {
            log.error("Book not supported: malformed conf file for " + internal + " no " + ConfigEntryType.MOD_DRV.getName() + " found"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            supported = false;
            return;
        }

        moduleType = ModuleType.getModuleType(modTypeName);
        if (getModuleType() == null)
        {
            log.error("Book not supported: malformed conf file for " + internal + " no module type found"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            supported = false;
            return;
        }

        BookType type = getModuleType().getBookType();
        if (type == null)
        {
            // We plan to add RawGenBook at a later time. So we don't need to be reminded all the time.
            if (!modTypeName.equals("RawGenBook")) //$NON-NLS-1$
            {
                log.debug("Book not supported: " + internal + " because missing book type for ModuleType (" + modTypeName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
            supported = false;
            return;
        }

        add(ConfigEntryType.KEY, type.toString());
    }

    private void adjustName()
    {
        // If there is no name then use the internal name
        if (table.get(ConfigEntryType.DESCRIPTION) == null)
        {
            log.error("Malformed conf file for " + internal + " no " + ConfigEntryType.DESCRIPTION.getName() + " found. Using internal of " + internal); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            add(ConfigEntryType.DESCRIPTION, internal);
        }
    }

    /**
     * Determine which books are not supported.
     * Also, report on problems.
     */
    private void validate()
    {
        // Locked modules are described but don't exist.
        if (getValue(ConfigEntryType.CIPHER_KEY) != null)
        {
            // Don't need to say we don't support locked modules. We already know that!
            //log.debug("Book not supported: " + internal + " because it is locked."); //$NON-NLS-1$ //$NON-NLS-2$
            supported = false;
            return;
        }
    }

    /**
     * Build an ordered map so that it displays in a consistent order.
     */
    private void toOSIS(OSISUtil.ObjectFactory factory, Element ele, String aTitle, ConfigEntryType[] category)
    {
        Element title = null;
        for (int i = 0; i < category.length; i++)
        {
            ConfigEntry entry = (ConfigEntry) table.get(category[i]);
            Element configElement = null;
            
            if (entry != null)
            {
                configElement = entry.toOSIS();
            }

            if (title == null && configElement != null)
            {
                // I18N(DMS): use aTitle to lookup translation. 
                title = factory.createHeader();
                title.addContent(aTitle); 
                ele.addContent(title);
            }

            if (configElement != null)
            {
                ele.addContent(configElement);
            }
        }
    }

    /**
     * These are the elements that JSword requires.
     * They are a superset of those that Sword requires.
     */
    public static final ConfigEntryType[] REQUIRED =
    {
        ConfigEntryType.INITIALS,
        ConfigEntryType.DESCRIPTION,
        ConfigEntryType.KEY,
        ConfigEntryType.DATA_PATH,
        ConfigEntryType.MOD_DRV,
    };

    public static final ConfigEntryType[] BASIC_INFO =
    {
        ConfigEntryType.INITIALS,
        ConfigEntryType.DESCRIPTION,
        ConfigEntryType.KEY,
        ConfigEntryType.LCSH,
        ConfigEntryType.CATEGORY,
        ConfigEntryType.VERSION,
        ConfigEntryType.SWORD_VERSION_DATE,
        ConfigEntryType.HISTORY,
    };

    public static final ConfigEntryType[] LANG_INFO =
    {
        ConfigEntryType.LANGUAGE,
        ConfigEntryType.LANG,
        ConfigEntryType.LANGUAGE_FROM,
        ConfigEntryType.GLOSSARY_FROM,
        ConfigEntryType.LANGUAGE_TO,
        ConfigEntryType.GLOSSARY_TO,
    };

    public static final ConfigEntryType[] COPYRIGHT_INFO =
    {
        ConfigEntryType.ABOUT,
        ConfigEntryType.DISTRIBUTION_LICENSE,
        ConfigEntryType.DISTRIBUTION_NOTES,
        ConfigEntryType.DISTRIBUTION_SOURCE,
        ConfigEntryType.COPYRIGHT,
        ConfigEntryType.COPYRIGHT_DATE,
        ConfigEntryType.COPYRIGHT_HOLDER,
        ConfigEntryType.COPYRIGHT_CONTACT_NAME,
        ConfigEntryType.COPYRIGHT_CONTACT_ADDRESS,
        ConfigEntryType.COPYRIGHT_CONTACT_EMAIL,
        ConfigEntryType.COPYRIGHT_NOTES,
        ConfigEntryType.TEXT_SOURCE,
    };

    public static final ConfigEntryType[] FEATURE_INFO =
    {
        ConfigEntryType.FEATURE,
        ConfigEntryType.GLOBAL_OPTION_FILTER,
        ConfigEntryType.FONT,
    };

    public static final ConfigEntryType[] SYSTEM_INFO =
    {
        ConfigEntryType.DATA_PATH,
        ConfigEntryType.MOD_DRV,
        ConfigEntryType.SOURCE_TYPE,
        ConfigEntryType.BLOCK_TYPE,
        ConfigEntryType.COMPRESS_TYPE,
        ConfigEntryType.ENCODING,
        ConfigEntryType.MINIMUM_VERSION,
        ConfigEntryType.DIRECTION,
    };

    /**
     * The log stream
     */
    private static final Logger log = Logger.getLogger(ConfigEntryTable.class);

    /**
     * True if this module's config type can be used by JSword.
     */
    private boolean supported;

    /**
     * The ModuleType for this ConfigEntry
     */
    private ModuleType moduleType;

    /**
     * A map of lists of known config entries.
     */
    private Map table = new HashMap();

    /**
     * The original name of this config file from mods.d.
     * This is only used for managing warnings and errors
     */
    private String internal;

    /**
     * A helper for the reading of the conf file.
     */
    private String readahead;
}

Index: SwordConstants.java
===================================================================
RCS file: /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword/SwordConstants.java,v
retrieving revision 1.27
retrieving revision 1.28
diff -C2 -d -r1.27 -r1.28
*** SwordConstants.java	5 Oct 2004 22:03:09 -0000	1.27
--- SwordConstants.java	28 Feb 2005 02:21:40 -0000	1.28
***************
*** 95,262 ****
  
      /**
-      * Constants for global option filter
-      */
-     static final int GOF_GBF_STRONGS = 1;
-     static final int GOF_GBF_FOOTNOTES = 1 << 1;
-     static final int GOF_GBF_SCRIPREF = 1 << 2;
-     static final int GOF_GBF_MORPH = 1 << 3;
-     static final int GOF_GBF_HEADINGS = 1 << 4;
-     static final int GOF_THML_STRONGS = 1 << 5;
-     static final int GOF_THML_FOOTNOTES = 1 << 6;
-     static final int GOF_THML_SCRIPREF = 1 << 7;
-     static final int GOF_THML_MORPH = 1 << 8;
-     static final int GOF_THML_HEADINGS = 1 << 9;
-     static final int GOF_THML_VARIANTS = 1 << 10;
-     static final int GOF_THML_LEMMA = 1 << 11;
-     static final int GOF_UTF8_CANTILLATION = 1 << 12;
-     static final int GOF_UTF8_GREEK_ACCENTS = 1 << 13;
-     static final int GOF_UTF8_HEBREW_VOWELS = 1 << 14;
- 
-     /**
-      * Strings for global option filter
-      */
-     static final String[] GOF_STRINGS =
-     {
-         "GBFStrongs", //$NON-NLS-1$
-         "GBFFootnotes", //$NON-NLS-1$
-         "GBFScripref", //$NON-NLS-1$
-         "GBFMorph", //$NON-NLS-1$
-         "GBFHeadings", //$NON-NLS-1$
-         "GBFRedLetterWords", //$NON-NLS-1$
-         "ThMLStrongs", //$NON-NLS-1$
-         "ThMLFootnotes", //$NON-NLS-1$
-         "ThMLScripref", //$NON-NLS-1$
-         "ThMLMorph", //$NON-NLS-1$
-         "ThMLHeadings", //$NON-NLS-1$
-         "ThMLVariants", //$NON-NLS-1$
-         "THMLLemma", //$NON-NLS-1$
-         "UTF8Cantillation", //$NON-NLS-1$
-         "UTF8GreekAccents", //$NON-NLS-1$
-         "UTF8HebrewVowels", //$NON-NLS-1$
-         "OSISStrongs", //$NON-NLS-1$
-         "OSISFootnotes", //$NON-NLS-1$
-         "OSISMorph", //$NON-NLS-1$
-         "OSISHeadings", //$NON-NLS-1$
-         "OSISRedLetterWords", //$NON-NLS-1$
-     };
- 
-     /**
-      * Constants for direction
-      */
-     static final int DIRECTION_LTOR = 0;
-     static final int DIRECTION_RTOL = 1;
-     static final int DIRECTION_BIDI = 2;
- 
-     /**
-      * Strings for direction
-      */
-     static final String[] DIRECTION_STRINGS =
-     {
-         "LtoR", //$NON-NLS-1$
-         "RtoL", //$NON-NLS-1$
-         "bidi" //$NON-NLS-1$
-     };
- 
-     /**
-      * Constants for source type
-      */
-     static final int SOURCE_PLAINTEXT = 0;
-     static final int SOURCE_GBF = 1;
-     static final int SOURCE_THML = 2;
-     static final int SOURCE_OSIS = 3;
- 
-     /**
-      * Strings for source type
-      */
-     static final String[] SOURCE_STRINGS =
-     {
-         "Plaintext", //$NON-NLS-1$
-         "GBF", //$NON-NLS-1$
-         "ThML", //$NON-NLS-1$
-         "OSIS", //$NON-NLS-1$
-     };
- 
-     /**
-      * constants for encoding
-      */
-     static final int ENCODING_LATIN1 = 0;
-     static final int ENCODING_UTF8 = 1;
- 
-     /**
-      * Strings for encoding
-      */
-     static final String[] ENCODING_STRINGS =
-     {
-         "Latin-1", //$NON-NLS-1$
-         "UTF-8", //$NON-NLS-1$
-         "SCSU", //$NON-NLS-1$
-         "UTF-16" //$NON-NLS-1$
-     };
- 
-     static final String[] ENCODING_JAVA =
-     {
-         "ISO-8859-1", //$NON-NLS-1$
-         "UTF-8", //$NON-NLS-1$
-         "SCSU", //$NON-NLS-1$
-         "UTF-16", //$NON-NLS-1$
-     };
- 
-     /**
-      * Constants for feature
-      */
-     static final int FEATURE_STRONGS_NUMBERS = 1;
-     static final int FEATURE_GREEK_DEF = 1 << 1;
-     static final int FEATURE_HEBREW_DEF = 1 << 2;
-     static final int FEATURE_GREEK_PARSE = 1 << 3;
-     static final int FEATURE_HEBREW_PARSE = 1 << 4;
-     static final int FEATURE_DAILY_DEVOTION = 1 << 5;
-     static final int FEATURE_GLOSSARY = 1 << 6;
- 
-     /**
-      * Strings for feature
-      */
-     static final String[] FEATURE_STRINGS =
-     {
-         "StrongsNumbers", //$NON-NLS-1$
-         "GreekDef", //$NON-NLS-1$
-         "HebrewDef", //$NON-NLS-1$
-         "GreekParse", //$NON-NLS-1$
-         "HebrewParse", //$NON-NLS-1$
-         "DailyDevotion", //$NON-NLS-1$
-         "Glossary", //$NON-NLS-1$
-     };
-     
-     /**
-      * public domain
-      */
-     static final int DISTRIBUTION_LICENSE_PD = 1;
- 
-     /**
-      * copyrighted but free for distribution
-      */
-     static final int DISTRIBUTION_LICENSE_CBFFD = 1 << 1;
- 
-     /**
-      * copyrighted by licensed for distribution by crosswire
-      */
-     static final int DISTRIBUTION_LICENSE_CBLFDBC = 1 << 2;
- 
-     /**
-      * copyrighted
-      */
-     static final int DISTRIBUTION_LICENSE_C = 1 << 3;
- 
-     /**
-      * Strings for distribution license
-      */
-     static final String[] DISTIBUTION_LICENSE_STRINGS =
-     {
-         "public domain", //$NON-NLS-1$
-         "copyrighted but free for distribution", //$NON-NLS-1$
-         "copyrighted by licensed for distribution by crosswire", //$NON-NLS-1$
-         "copyrighted" //$NON-NLS-1$
-     };
- 
-     /**
       * Get the testament of a given verse
       */
--- 95,98 ----
***************
*** 307,325 ****
  
      /**
!      * array containing LUT of offsets in the chapter table.
       */
!     private static int[][] bks;
  
      /**
!      * array containing LUT of positions of initial verses per chapter.
!      * This and all the cps* below were longs but I have no idea why so I made
!      * them all ints
       */
!     private static int[][] cps;
  
      /**
!      * The start of the new testament
       */
!     static final int ORDINAL_MAT11 = new Verse(BibleInfo.Names.MATTHEW, 1, 1, true).getOrdinal();
  
      /**
--- 143,162 ----
  
      /**
!      * The start of the new testament
       */
!     static final int ORDINAL_MAT11 = new Verse(BibleInfo.Names.MATTHEW, 1, 1, true).getOrdinal();
  
      /**
!      * array containing LUT of offsets in the chapter table.
       */
!     private static short[][] bks;
  
      /**
!      * array containing LUT of positions of initial verses per chapter.
!      * This and all the cps* below were longs and then ints
!      * This was an artifact of a port from C/C++ where int/long vary
!      * in size dependeing on architecture.
       */
!     private static short[][] cps;
  
      /**
***************
*** 332,339 ****
      static
      {
!         bks = new int[3][];
!         cps = new int[3][];
  
!         int[] bksot =
          {
              0, 1, 52, 93, 121, 158,
--- 169,176 ----
      static
      {
!         bks = new short[3][];
!         cps = new short[3][];
  
!         short[] bksot =
          {
              0, 1, 52, 93, 121, 158,
***************
*** 345,349 ****
          };
  
!         int[] bksnt =
          {
              0, 1, 30, 47, 72, 94,
--- 182,186 ----
          };
  
!         short[] bksnt =
          {
              0, 1, 30, 47, 72, 94,
***************
*** 357,361 ****
          bks[SwordConstants.TESTAMENT_NEW] = bksnt;
  
!         int[] cpsot =
          {
              0, 2, 3, 35, 61, 86,
--- 194,198 ----
          bks[SwordConstants.TESTAMENT_NEW] = bksnt;
  
!         short[] cpsot =
          {
              0, 2, 3, 35, 61, 86,
***************
*** 500,504 ****
          };
  
!         int[] cpsnt =
          {
              0, 2, 3, 29, 53, 71,
--- 337,341 ----
          };
  
!         short[] cpsnt =
          {
              0, 2, 3, 29, 53, 71,

--- NEW FILE: ConfigEntryType.java ---
package org.crosswire.jsword.book.sword;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import org.crosswire.jsword.book.BookType;
import org.crosswire.jsword.book.basic.AbstractBookMetaData;


/**
 * Constants for the keys in a SwordConfig file.
 * Taken from
 * http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout
 * now located at
 * http://www.crosswire.org/ucgi-bin/twiki/view/Swordapi/ConfFileLayout
 * 
[...1390 lines suppressed...]
                    INSTALL_SIZE,
                    FEATURE,
                    FONT,
                    LANG,
                    LCSH,
                    MOD_DRV,
                    MINIMUM_VERSION,
                    OBSOLETES,
                    SOURCE_TYPE,
                    SWORD_VERSION_DATE,
                    TEXT_SOURCE,
                    VERSION,
                    OSIS_Q_TO_TICK,
                    INITIALS,
                    KEY,
                    LANGUAGE,
                    LANGUAGE_FROM,
                    LANGUAGE_TO,
    };
}

Index: SwordBookDriver.java
===================================================================
RCS file: /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword/SwordBookDriver.java,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** SwordBookDriver.java	9 Oct 2004 21:45:05 -0000	1.35
--- SwordBookDriver.java	28 Feb 2005 02:21:40 -0000	1.36
***************
*** 62,65 ****
--- 62,67 ----
      public BookMetaData[] getBookMetaDatas()
      {
+         ConfigEntry.resetStatistics();
+ 
          List valid = new ArrayList();
  
***************
*** 121,124 ****
--- 123,128 ----
          }
  
+         ConfigEntry.dumpStatistics();
+ 
          return (BookMetaData[]) valid.toArray(new BookMetaData[valid.size()]);
      }
***************
*** 320,324 ****
                      Properties prop = new Properties();
                      prop.load(new FileInputStream(sysconfig));
!                     String datapath = prop.getProperty(ConfigEntry.DATA_PATH.toString());
                      testDefaultPath(reply, datapath);
                  }
--- 324,328 ----
                      Properties prop = new Properties();
                      prop.load(new FileInputStream(sysconfig));
!                     String datapath = prop.getProperty(ConfigEntryType.DATA_PATH.toString());
                      testDefaultPath(reply, datapath);
                  }

Index: CompressionType.java
===================================================================
RCS file: /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword/CompressionType.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** CompressionType.java	24 Jan 2005 23:29:45 -0000	1.4
--- CompressionType.java	28 Feb 2005 02:21:40 -0000	1.5
***************
*** 43,47 ****
          protected Backend getBackend(SwordBookMetaData sbmd, String path) throws BookException
          {
!             BlockType blockType = BlockType.fromString(sbmd.getProperty(ConfigEntry.BLOCK_TYPE));
              return new GZIPBackend(path, blockType);
          }
--- 43,47 ----
          protected Backend getBackend(SwordBookMetaData sbmd, String path) throws BookException
          {
!             BlockType blockType = BlockType.fromString(sbmd.getProperty(ConfigEntryType.BLOCK_TYPE));
              return new GZIPBackend(path, blockType);
          }

Index: ConfigEntry.java
===================================================================
RCS file: /cvs/jsword/jsword/java/jsword/org/crosswire/jsword/book/sword/ConfigEntry.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** ConfigEntry.java	24 Jan 2005 23:29:45 -0000	1.8
--- ConfigEntry.java	28 Feb 2005 02:21:40 -0000	1.9
***************
*** 1,48 ****
  package org.crosswire.jsword.book.sword;
  
! import java.io.Serializable;
  
  
  /**
!  * Constants for the keys in a SwordConfig file.
!  * Taken from
!  * http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout
!  * 
!  * <p>Keys that might be available that we are ignoring for now:
!  * <li>DistributionLicense: single value coded string, a ';' separated string of license attributes
!  *     There are mis-spellings, etc. so there is a need for a distributionLicenseAdditionalInfo field. (Ugh!)
!  *     See also SwordConstants.DISTIBUTION_LICENSE_STRINGS.
!  *     <pre>
!  *     // Returns the distributionLicense - this is a 'flag type' field - the value
!  *     // will be the result of several constants ORed. See the
!  *     // DISTRIBUTION_LICENSE* constants in SwordConstants. It appears some
!  *     // versions do not stick to this convention, because of this, there is an
!  *     // additional menber distributionLicenseAdditionInfo, to store additional
!  *     // information.
!  *     private int distributionLicense;
!  *     private String distributionLicenseAdditionalInfo = "";
!  *     String licensesString = reader.getFirstValue("DistributionLicense");
!  *     if (licensesString != null)
!  *     {
!  *         StringTokenizer tok = new StringTokenizer(licensesString, ";");
!  *         while (tok.hasMoreTokens())
!  *         {
!  *             String distributionLicenseString = tok.nextToken().trim();
!  *             int index = matchingIndex(SwordConstants.DISTIBUTION_LICENSE_STRINGS, distributionLicenseString, -1);
!  *             if (index != -1)
!  *             {
!  *                 distributionLicense |= 1 << index;
!  *             }
!  *             else
!  *             {
!  *                 if (!distributionLicenseAdditionalInfo.equals(""))
!  *                 {
!  *                     distributionLicenseAdditionalInfo += "; ";
!  *                 }
!  *                 distributionLicenseAdditionalInfo += distributionLicenseString;
!  *             }
!  *         }
!  *     }
!  *     </pre>
   * 
   * <p><table border='1' cellPadding='3' cellSpacing='0'>
--- 1,17 ----
  package org.crosswire.jsword.book.sword;
  
! import java.util.ArrayList;
! import java.util.Iterator;
! import java.util.List;
  
+ import org.crosswire.common.util.Histogram;
+ import org.crosswire.common.util.Logger;
+ import org.crosswire.common.util.StringUtil;
+ import org.crosswire.common.xml.XMLUtil;
+ import org.crosswire.jsword.book.OSISUtil;
+ import org.jdom.Element;
  
  /**
!  * A ConfigEntry holds the value(s) for an entry of ConfigEntryType.
   * 
   * <p><table border='1' cellPadding='3' cellSpacing='0'>
***************
*** 64,519 ****
   * </font></td></tr></table>
   * @see gnu.gpl.Licence
!  * @author Joe Walker [joe at eireneh dot com]
   * @version $Id$
   */
! public class ConfigEntry implements Serializable
  {
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry ABOUT = new ConfigEntry("About"); //$NON-NLS-1$
- 
-     /**
-      * single value integer, unknown use, some indications that we ought to be using it
-      */
-     public static final ConfigEntry BLOCK_COUNT = new ConfigEntry("BlockCount"); //$NON-NLS-1$
- 
-     /**
-      * The block type in use
-      */
-     public static final ConfigEntry BLOCK_TYPE = new ConfigEntry("BlockType"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry CATEGORY = new ConfigEntry("Category"); //$NON-NLS-1$
- 
-     /**
-      * single value string, for encryption
-      */
-     public static final ConfigEntry CIPHER_KEY = new ConfigEntry("CipherKey"); //$NON-NLS-1$
- 
-     /**
-      * The type of compression in use
-      */
-     public static final ConfigEntry COMPRESS_TYPE = new ConfigEntry("CompressType"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT = new ConfigEntry("Copyright"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT_CONTACT_ADDRESS = new ConfigEntry("CopyrightContactAddress"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT_CONTACT_EMAIL = new ConfigEntry("CopyrightContactEmail"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT_CONTACT_NAME = new ConfigEntry("CopyrightContactName"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT_DATE = new ConfigEntry("CopyrightDate"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT_HOLDER = new ConfigEntry("CopyrightHolder"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry COPYRIGHT_NOTES = new ConfigEntry("CopyrightNotes"); //$NON-NLS-1$
- 
-     /**
-      * Relative path to the data files, some issues with this
-      */
-     public static final ConfigEntry DATA_PATH = new ConfigEntry("DataPath"); //$NON-NLS-1$
- 
-     /**
-      * The full name of this module
-      */
-     public static final ConfigEntry DESCRIPTION = new ConfigEntry("Description"); //$NON-NLS-1$
- 
-     /**
-      * single value choice, from SwordConstants.DIRECTION_STRINGS we should probably use it?
-      */
-     public static final ConfigEntry DIRECTION = new ConfigEntry("Direction"); //$NON-NLS-1$
- 
-     /**
-      * single value integer, unknown use, some indications that we ought to be using it
-      */
-     public static final ConfigEntry DISPLAY_LEVEL = new ConfigEntry("DisplayLevel"); //$NON-NLS-1$
- 
-     /**
-      * unknown
-      */
-     public static final ConfigEntry DISTRIBUTION = new ConfigEntry("Distribution"); //$NON-NLS-1$
- 
-     /**
-      * single value coded string, a ';' separated string of license attributes
-      */
-     public static final ConfigEntry DISTRIBUTION_LICENSE = new ConfigEntry("DistributionLicense"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry DISTRIBUTION_NOTES = new ConfigEntry("DistributionNotes"); //$NON-NLS-1$
- 
-     /**
-      * single value string, unknown use
-      */
-     public static final ConfigEntry DISTRIBUTION_SOURCE = new ConfigEntry("DistributionSource"); //$NON-NLS-1$
- 
-     /**
-      * The character encoding
-      */
-     public static final ConfigEntry ENCODING = new ConfigEntry("Encoding"); //$NON-NLS-1$
- 
-     /**
-      * multiple value flagset, from SwordConstants.GOF_STRINGS
-      */
-     public static final ConfigEntry GLOBAL_OPTION_FILTER = new ConfigEntry("GlobalOptionFilter"); //$NON-NLS-1$
- 
-     /**
-      * unknown
-      */
-     public static final ConfigEntry GLOSSARY_FROM = new ConfigEntry("GlossaryFrom"); //$NON-NLS-1$
- 
-     /**
-      * unknown
-      */
-     public static final ConfigEntry GLOSSARY_TO = new ConfigEntry("GlossaryTo"); //$NON-NLS-1$
  
      /**
!      * multiple values starting with History, some sort of change-log?
!      */
!     public static final ConfigEntry HISTORY = new ConfigEntry("History"); //$NON-NLS-1$
! 
!     /**
!      * single value integer, the installed size (in bytes?)
!      */
!     public static final ConfigEntry INSTALL_SIZE = new ConfigEntry("InstallSize"); //$NON-NLS-1$
! 
!     /**
!      * multiple value flagset, from SwordConstants.FEATURE_STRINGS
!      */
!     public static final ConfigEntry FEATURE = new ConfigEntry("Feature"); //$NON-NLS-1$
! 
!     /**
!      * single value string
!      */
!     public static final ConfigEntry FONT = new ConfigEntry("Font"); //$NON-NLS-1$
! 
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_0_1 = new ConfigEntry("History_0.1"); //$NON-NLS-1$
! 
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_0_2 = new ConfigEntry("History_0.2"); //$NON-NLS-1$
! 
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_0_3 = new ConfigEntry("History_0.3"); //$NON-NLS-1$
! 
!     /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_0_9 = new ConfigEntry("History_0.9"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_0_91 = new ConfigEntry("History_0.91"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_0_92 = new ConfigEntry("History_0.92"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_0 = new ConfigEntry("History_1.0"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_1 = new ConfigEntry("History_1.1"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_2 = new ConfigEntry("History_1.2"); //$NON-NLS-1$
! 
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_3 = new ConfigEntry("History_1.3"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_4 = new ConfigEntry("History_1.4"); //$NON-NLS-1$
! 
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_5 = new ConfigEntry("History_1.5"); //$NON-NLS-1$
! 
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_6 = new ConfigEntry("History_1.6"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_7 = new ConfigEntry("History_1.7"); //$NON-NLS-1$
  
      /**
!      * unknown
       */
!     public static final ConfigEntry HISTORY_1_8 = new ConfigEntry("History_1.8"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_1_9 = new ConfigEntry("History_1.9"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_2_0 = new ConfigEntry("History_2.0"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_2_1 = new ConfigEntry("History_2.1"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_2_2 = new ConfigEntry("History_2.2"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry HISTORY_2_5 = new ConfigEntry("History_2.5"); //$NON-NLS-1$
  
!     /**
!      * single value string, defaults to en, the language of the module
!      */
!     public static final ConfigEntry LANG = new ConfigEntry("Lang"); //$NON-NLS-1$
  
!     /**
!      * unknown
       */
!     public static final ConfigEntry LCSH = new ConfigEntry("LCSH"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry LEXICON_FROM = new ConfigEntry("LexiconFrom"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry LEXICON_TO = new ConfigEntry("LexiconTo"); //$NON-NLS-1$
  
!     /**
!      * The type of module
       */
!     public static final ConfigEntry MOD_DRV = new ConfigEntry("ModDrv"); //$NON-NLS-1$
  
!     /**
!      * single value version number, lowest sword c++ version that can read this module
       */
!     public static final ConfigEntry MINIMUM_VERSION = new ConfigEntry("MinimumVersion"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry MINIMUM_SWORD_VERSION = new ConfigEntry("MinimumSwordVersion"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry OBSOLETES = new ConfigEntry("Obsoletes"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry SOURCE_TYPE = new ConfigEntry("SourceType"); //$NON-NLS-1$
  
!     /**
!      * unknown
!      */
!     public static final ConfigEntry SWORD_VERSION_DATE = new ConfigEntry("SwordVersionDate"); //$NON-NLS-1$
  
!     /**
!      * single value string, unknown use
!      */
!     public static final ConfigEntry TEXT_SOURCE = new ConfigEntry("TextSource"); //$NON-NLS-1$
  
!     /**
!      * single value string, unknown use
!      */
!     public static final ConfigEntry VERSION = new ConfigEntry("Version"); //$NON-NLS-1$
  
!     /**
!      * Simple ctor
!      */
!     private ConfigEntry(String name)
!     {
!         this.name = name;
!     }
  
!     /**
!      * Lookup method to convert from a String
!      */
!     public static ConfigEntry fromString(String name)
!     {
!         for (int i = 0; i < VALUES.length; i++)
          {
!             ConfigEntry o = VALUES[i];
!             if (o.name.equalsIgnoreCase(name))
              {
!                 return o;
              }
          }
-         // should not get here.
-         // But there are typos in the keys in the module conf files
-         return null;
-     }
- 
-     /**
-      * Lookup method to convert from an integer
-      */
-     public static ConfigEntry fromInteger(int i)
-     {
-         return VALUES[i];
-     }
  
!     /**
!      * Prevent subclasses from overriding canonical identity based Object methods
!      * @see java.lang.Object#equals(java.lang.Object)
!      */
!     public final boolean equals(Object o)
!     {
!         return super.equals(o);
!     }
! 
!     /**
!      * Prevent subclasses from overriding canonical identity based Object methods
!      * @see java.lang.Object#hashCode()
!      */
!     public final int hashCode()
!     {
!         return super.hashCode();
      }
! 
!     /* (non-Javadoc)
!      * @see java.lang.Object#toString()
!      */
!     public String toString()
      {
!         return name;
      }
  
      /**
!      * The name of the ConfigEntry
       */
!     private String name;
! 
      /**
!      * Serialization ID
       */
!     private static final long serialVersionUID = 3258125873411273014L;
! 
!     // Support for serialization
!     private static int nextObj;
!     private final int obj = nextObj++;
! 
!     Object readResolve()
!     {
!         return VALUES[obj];
!     }
  
!     private static final ConfigEntry[] VALUES =
!     {
!                     ABOUT,
!                     BLOCK_COUNT,
!                     BLOCK_TYPE,
!                     CATEGORY,
!                     CIPHER_KEY,
!                     COMPRESS_TYPE,
!                     COPYRIGHT,
!                     COPYRIGHT_CONTACT_ADDRESS,
!                     COPYRIGHT_CONTACT_EMAIL,
!                     COPYRIGHT_CONTACT_NAME,
!                     COPYRIGHT_DATE,
!                     COPYRIGHT_HOLDER,
!                     COPYRIGHT_NOTES,
!                     DATA_PATH,
!                     DESCRIPTION,
!                     DIRECTION,
!                     DISPLAY_LEVEL,
!                     DISTRIBUTION,
!                     DISTRIBUTION_LICENSE,
!                     DISTRIBUTION_NOTES,
!                     DISTRIBUTION_SOURCE,
!                     ENCODING,
!                     GLOBAL_OPTION_FILTER,
!                     GLOSSARY_FROM,
!                     GLOSSARY_TO,
!                     HISTORY,
!                     INSTALL_SIZE,
!                     FEATURE,
!                     FONT,
!                     HISTORY_0_1,
!                     HISTORY_0_2,
!                     HISTORY_0_3,
!                     HISTORY_0_9,
!                     HISTORY_0_91,
!                     HISTORY_0_92,
!                     HISTORY_1_0,
!                     HISTORY_1_1,
!                     HISTORY_1_2,
!                     HISTORY_1_3,
!                     HISTORY_1_4,
!                     HISTORY_1_5,
!                     HISTORY_1_6,
!                     HISTORY_1_7,
!                     HISTORY_1_8,
!                     HISTORY_1_9,
!                     HISTORY_2_0,
!                     HISTORY_2_1,
!                     HISTORY_2_2,
!                     HISTORY_2_5,
!                     LANG,
!                     LCSH,
!                     LEXICON_FROM,
!                     LEXICON_TO,
!                     MOD_DRV,
!                     MINIMUM_VERSION,
!                     MINIMUM_SWORD_VERSION,
!                     OBSOLETES,
!                     SOURCE_TYPE,
!                     SWORD_VERSION_DATE,
!                     TEXT_SOURCE,
!                     VERSION,
!     };
  }
--- 33,390 ----
   * </font></td></tr></table>
   * @see gnu.gpl.Licence
!  * @author DM Smith [ dmsmith555 at hotmail dot com]
   * @version $Id$
   */
! public class ConfigEntry
  {
  
      /**
!      * Create a ConfigEntry whose type is not certain
!      * and whose value is not known.
!      * @param moduleName the internal name of the module
!      * @param aName the name of the ConfigEntry.
       */
!     public ConfigEntry(String moduleName, String aName)
!     {
!         internal = moduleName;
!         name = aName;
!         type = ConfigEntryType.fromString(aName);
!     }
  
      /**
!      * Create a ConfigEntry directly with an initial value.
!      * @param moduleName the internal name of the module
!      * @param aType the kind of ConfigEntry
!      * @param aValue the initial value for the ConfigEntry
       */
!     public ConfigEntry(String moduleName, ConfigEntryType aType, String aValue)
!     {
!         internal = moduleName;
!         name = aType.getName();
!         type = aType;
!         addValue(aValue);
!     }
  
      /**
!      * Get the key of this ConfigEntry
       */
!     public String getName()
!     {
!         if (type != null)
!         {
!             return type.getName();
!         }
!         return name;
!     }
  
      /**
!      * Get the type of this ConfigEntry
       */
!     public ConfigEntryType getType()
!     {
!         return type;
!     }
  
      /**
!      * Determines whether the string is allowed. For some config entries, 
!      * the value is expected to be one of a group, for others the format is defined.
!      * 
!      * @param aValue
!      * @return true if the string is allowed
       */
!     public boolean isAllowed(String aValue)
!     {
!         if (type != null)
!         {
!             return type.isAllowed(aValue);
!         }
!         return true;
!     }
  
      /**
!      * RTF is allowed in a few config entries.
!      * @return true if rtf is allowed
       */
!     public boolean allowsRTF()
!     {
!         if (type != null)
!         {
!             return type.allowsRTF();
!         }
!         return true;
!     }
!     
      /**
!      * While most fields are single line or single value, some allow continuation.
!      * A continuation mark is a backslash at the end of a line. It is not to be followed by whitespace.
!      * @return true if continuation is allowed
       */
!     public boolean allowsContinuation()
!     {
!         if (type != null)
!         {
!             return type.allowsContinuation();
!         }
!         return true;
!     }
  
      /**
!      * Some keys can repeat. When this happens each is a single value pick from a list of choices.
!      * @return true if this ConfigEntryType can occur more than once
       */
!     public boolean mayRepeat()
!     {
!         if (type != null)
!         {
!             return type.mayRepeat();
!         }
!         return true;
!     }
!     
      /**
!      * 
       */
!     public boolean reportDetails()
!     {
!         if (type != null)
!         {
!             return type.reportDetails();
!         }
!         return true;
!     }
      /**
!      * Determine whether this config entry is supported.
!      * @return true if this ConfigEntry has a type.
       */
!     public boolean isSupported()
!     {
!         return type != null;
!     }
  
      /**
!      * Get the value(s) of this ConfigEntry.
!      * If mayRepeat() == true then it returns a List.
!      * Otherwise it returns a string.
!      * @return a list, value or null.
       */
!     public Object getValue()
!     {
!         if (value != null)
!         {
!             return value;
!         }
!         if (values != null)
!         {
!             return values;
!         }
!         return type.getDefault();
!     }
  
      /**
!      * Add a value to the list of values for this ConfigEntry
       */
!     public void addValue(String aValue)
!     {
!         String confEntryName = getName();
!         aValue = handleRTF(aValue);
!         if (mayRepeat())
!         {
!             if (values == null)
!             {
!                 histogram.increment(confEntryName);
!                 values = new ArrayList();
!             }
!             if (reportDetails())
!             {
!                 histogram.increment(confEntryName + '.' + aValue);
!             }
!             if (!isAllowed(aValue))
!             {
!                 log.info("Ignoring unknown config value for " + confEntryName + " in " + internal + ": " + aValue); //$NON-NLS-1$ //$NON-NLS-2$  //$NON-NLS-3$
!                 return;
!             }
!             values.add(aValue);
!         }
!         else
!         {
!             if (value != null)
!             {
!                 log.info("Ignoring unexpected additional entry for " + confEntryName + " in " + internal + ": " + aValue); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$                
!             }
!             else
!             {
!                 histogram.increment(confEntryName);
!                 if (type.hasChoices())
!                 {
!                     histogram.increment(confEntryName + '.' + aValue);
!                 }
!                 if (!isAllowed(aValue))
!                 {
!                     log.info("Ignoring unknown config value for " + confEntryName + " in " + internal + ": " + aValue); //$NON-NLS-1$ //$NON-NLS-2$  //$NON-NLS-3$
!                     return;
!                 }
!                 value = aValue;
!             }
!         }
!     }
!     
!     public Element toOSIS()
!     {
!         OSISUtil.ObjectFactory factory = OSISUtil.factory();
!         
!         Element rowEle = factory.createRow();
  
!         Element nameEle = factory.createCell();
!         Element hiEle = factory.createHI();
!         hiEle.setAttribute("rend", "bold"); //$NON-NLS-1$ //$NON-NLS-2$
!         nameEle.addContent(hiEle);
!         Element valueElement = factory.createCell();
!         rowEle.addContent(nameEle);
!         rowEle.addContent(valueElement);
!         
!         // I18N(DMS): use name to lookup translation. 
!         hiEle.addContent(getName());
  
!         if (value != null)
!         {
!             String expandedValue = XMLUtil.escape(value);
!             if (allowsContinuation() || allowsRTF())
!             {
!                 valueElement.addContent(processLines(factory, expandedValue));
!             }
!             else
!             {
!                 valueElement.addContent(expandedValue);
!             }
!         }
  
!         if (values != null)
!         {
!             Element listEle = factory.createLG();
!             valueElement.addContent(listEle);
  
!             Iterator iter = values.iterator();
!             while (iter.hasNext())
!             {
!                 Element itemEle = factory.createL();
!                 listEle.addContent(itemEle);
!                 itemEle.addContent(XMLUtil.escape(iter.next().toString()));
!             }
!         }
!         return rowEle;
!     }
  
!     public static void resetStatistics()
!     {
!         histogram.clear();
!     }
  
!     public static void dumpStatistics()
!     {
!         // Uncomment the following line to produce statistics
!         //System.out.println(histogram.toString());
!     }
  
!     /* (non-Javadoc)
!      * @see java.lang.Object#equals(java.lang.Object)
       */
!     public boolean equals(Object obj)
!     {
!         // Since this can not be null
!         if (obj == null)
!         {
!             return false;
!         }
  
!         // Check that that is the same as this
!         // Don't use instanceOf since that breaks inheritance
!         if (!obj.getClass().equals(this.getClass()))
!         {
!             return false;
!         }
  
!         ConfigEntry that = (ConfigEntry) obj;
!         return that.getName().equals(this.getName());
!     }
  
!     /* (non-Javadoc)
!      * @see java.lang.Object#hashCode()
       */
!     public int hashCode()
!     {
!         return getName().hashCode();
!     }
  
!     /* (non-Javadoc)
!      * @see java.lang.Object#toString()
       */
!     public String toString()
!     {
!         return getName();
!     }
  
!     private String handleRTF(String aValue)
!     {
!         String copy = aValue;
!         // This method is a hack! It could be made much nicer.
  
!         // strip \pard
!         copy = copy.replaceAll("\\\\pard ?", ""); //$NON-NLS-1$ //$NON-NLS-2$
  
!         // replace rtf newlines
!         copy = copy.replaceAll("\\\\pa[er] ?", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  
!         // strip whatever \qc is.
!         copy = copy.replaceAll("\\\\qc ?", ""); //$NON-NLS-1$ //$NON-NLS-2$
  
!         // strip bold and italic
!         copy = copy.replaceAll("\\\\[bi]0? ?", ""); //$NON-NLS-1$ //$NON-NLS-2$
  
!         // strip unicode characters
!         copy = copy.replaceAll("\\\\u-?[0-9]{4,6}+\\?", ""); //$NON-NLS-1$ //$NON-NLS-2$
  
!         // strip { and } which are found in {\i text }
!         copy = copy.replaceAll("[{}]", ""); //$NON-NLS-1$ //$NON-NLS-2$
  
!         // This check is here rather than at the top, so we can find the problems
!         // and fix the source.
!         if (!allowsRTF())
          {
!             if (!copy.equals(aValue))
              {
!                 log.info("Ignoring unexpected RTF for " + getName() + " in " + internal + ": " + aValue); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
              }
+             return aValue;
          }
  
!         return copy;
      }
!     
!     private List processLines(OSISUtil.ObjectFactory factory, String aValue)
      {
!         List list = new ArrayList();
!         String [] lines = StringUtil.splitAll(aValue, '\n');
!         for (int i = 0; i < lines.length; i++)
!         {
!             Element lineElement = factory.createL();
!             lineElement.addContent(lines[i]);
!             list.add(lineElement);
!         }
!         return list;
      }
  
      /**
!      * The log stream
       */
!     private static final Logger log = Logger.getLogger(ConfigEntry.class);
      /**
!      * A histogram for debugging.
       */
!     private static Histogram histogram = new Histogram();
  
!     private ConfigEntryType type;
!     private String internal;
!     private String name;
!     private List values;
!     private String value;
  }



More information about the jsword-svn mailing list