1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 or later
5    * as published by the Free Software Foundation. This program is distributed
6    * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7    * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *      http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * © CrossWire Bible Society, 2005 - 2016
18   *
19   */
20  package org.crosswire.jsword.book.sword;
21  
22  /**
23   * A utility class for loading the entries in a Sword book's conf file. Since
24   * the conf files are manually maintained, there can be all sorts of errors in
25   * them. This class does robust checking and reporting.
26   * <p/>
27   * <p/>
28   * Config file format. See also: <a href=
29   * "http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout">
30   * http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout</a>
31   * <p/>
32   * <p/>
33   * The contents of the About field are in rtf.
34   * <p/>
35   * \ is used as a continuation line.
36   *
37   * @author Mark Goodwin
38   * @author Joe Walker
39   * @author Jacky Cheung
40   * @author DM Smith
41   * @see gnu.lgpl.License The GNU Lesser General Public License for details.<br>
42   * The copyright to this program is held by its authors.
43   */
44  public final class ConfigEntryTable {
45  
46      /**
47       * Create an empty Sword config for the named book.
48       *
49       * @param bookName the name of the book
50       * @param isRootConfig true to indicate a root configuration
51       */
52      public ConfigEntryTable(String bookName, boolean isRootConfig) {
53          this(bookName, isRootConfig, null);
54      }
55  
56      /**
57       * Sometimes, we're creating the config off another config, and so need to ensure the initials match exactly.
58       *
59       * @param bookName the name of the book
60       * @param isRootConfig true to indicate a root configuration
61       * @param initials the set of initials used to identify this module. This could be different to the bookName (which may be lowercase)
62       */
63      public ConfigEntryTable(String bookName, boolean isRootConfig, String initials) {
64          this.initials = initials == null ? bookName : initials;
65      }
66  
67     /**
68       * Build's a SWORD conf file as a string. The result is not identical to the
69       * original, cleaning up problems in the original and re-arranging the
70       * entries into a predictable order.
71       *
72       * @return the well-formed conf.
73       */
74      public String toConf() {
75          StringBuilder buf = new StringBuilder();
76          buf.append('[');
77          buf.append(initials);
78          buf.append("]\n");
79  /*
80          toConf(buf, BASIC_INFO);
81          toConf(buf, SYSTEM_INFO);
82          toConf(buf, HIDDEN);
83          toConf(buf, FEATURE_INFO);
84          toConf(buf, LANG_INFO);
85          toConf(buf, COPYRIGHT_INFO);
86          toConf(buf, extra);
87  */
88          return buf.toString();
89      }
90  
91  /*    
92      protected void report() {
93          Iterator<String> kIter = config.keySet().iterator();
94          while (kIter.hasNext()) {
95              String key = kIter.next();
96              Iterator<String> vIter = config.getValues(key).iterator();
97              while (vIter.hasNext()) {
98                  String value = vIter.next();
99  
100                 // Only CIPHER_KEYS that are empty are not ignored
101                 if (value.length() == 0 && !SwordBookMetaData.KEY_CIPHER_KEY.equalsIgnoreCase(key)) {
102                     log.warn("Ignoring empty entry in [{}]{}=", initials, key);
103                     continue;
104                 }
105 
106                 // Create a configEntry so that the name is normalized.
107                 ConfigEntry configEntry = new ConfigEntry(initials, key);
108                 ConfigEntryType type = configEntry.getType();
109                 ConfigEntry e = null; // table.get(type);
110 
111                 if (e == null) {
112                     if (type == null) {
113                         log.warn("Extra entry in [{}]{}", initials, configEntry.getName());
114 //                        extra.put(key, configEntry);
115                     } else if (type.isSynthetic()) {
116                         log.warn("Ignoring unexpected entry in [{}]{}", initials, configEntry.getName());
117                     } else {
118 //                        table.put(type, configEntry);
119                     }
120                 } else {
121                     configEntry = e;
122                 }
123                 // History is a special case it is of the form History_x.x
124                 // The config entry is History without the x.x.
125                 // We want to put x.x at the beginning of the string
126                 if (ConfigEntryType.HISTORY.equals(type)) {
127                     int pos = key.indexOf('_');
128                     value = key.substring(pos + 1) + ' ' + value;
129                 }
130 
131                 configEntry.addValue(value);
132 
133                 // Filter known types of entries
134                 if (type != null) {
135                     value = type.filter(value);
136 
137                     // Report on fields that shouldn't have RTF but do
138                     if (!type.allowsRTF() && RTF_PATTERN.matcher(value).find()) {
139                         log.info("Unexpected RTF for [{}]{} = {}", initials, key, value);
140                     }
141 
142                     if (type.mayRepeat()) {
143                         if (!type.isAllowed(value)) {
144                             log.info("Unknown config value for [{}]{} = {}", initials, key, value);
145                         }
146                     } else {
147                         if (value != null) {
148                             log.info("Ignoring unexpected additional entry for [{}]{} = {}", initials, key, value);
149                         } else {
150                             if (!type.isAllowed(value)) {
151                                 log.info("Unknown config value for [{}]{} = {}", initials, key, value);
152                             }
153                         }
154                     }
155                 }
156             }
157         }
158     }
159  */
160 
161     /**
162      * Build an ordered map so that it displays in a consistent order.
163      */
164     /*
165     private void toConf(StringBuilder buf, ConfigEntryType[] category) {
166         for (int i = 0; i < category.length; i++) {
167 
168             String entry = table.get(category[i]);
169 
170             if (entry != null && !category[i].isSynthetic()) {
171                 String text = entry.toConf();
172                 if (text != null && text.length() > 0) {
173                     buf.append(entry.toConf());
174                 }
175             }
176         }
177     }
178 */
179     /**
180      * Build an ordered map so that it displays in a consistent order.
181      */
182 /*
183     private void toConf(StringBuilder buf, Map<String, ConfigEntry> map) {
184         for (Map.Entry<String, ConfigEntry> mapEntry : map.entrySet()) {
185             ConfigEntry entry = mapEntry.getValue();
186             String text = entry.toConf();
187             if (text != null && text.length() > 0) {
188                 buf.append(text);
189             }
190         }
191     }
192 */
193 
194     /**
195      * The set of initials identifying this book
196      */
197     private String initials;
198 
199     /**
200      * A pattern of allowable RTF in a SWORD conf. These are: \pard, \pae, \par, \qc \b, \i and embedded Unicode
201      */
202     // private static final Pattern RTF_PATTERN = Pattern.compile("\\\\pard|\\\\pa[er]|\\\\qc|\\\\[bi]|\\\\u-?[0-9]{4,6}+");
203 
204 }
205