| SwordBookMetaData.java |
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 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * 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 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: SwordBookMetaData.java 2232 2012-02-20 01:21:42Z dmsmith $
21 */
22 package org.crosswire.jsword.book.sword;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.net.URI;
27 import java.util.List;
28 import java.util.Locale;
29
30 import org.crosswire.common.util.Language;
31 import org.crosswire.common.util.NetUtil;
32 import org.crosswire.common.util.PropertyMap;
33 import org.crosswire.jsword.book.BookCategory;
34 import org.crosswire.jsword.book.FeatureType;
35 import org.crosswire.jsword.book.KeyType;
36 import org.crosswire.jsword.book.basic.AbstractBookMetaData;
37 import org.crosswire.jsword.book.filter.Filter;
38 import org.crosswire.jsword.book.filter.FilterFactory;
39 import org.jdom.Document;
40
41 /**
42 * A utility class for loading and representing Sword book configs.
43 *
44 * <p>
45 * Config file format. See also: <a href=
46 * "http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout">
47 * http://sword.sourceforge.net/cgi-bin/twiki/view/Swordapi/ConfFileLayout</a>
48 *
49 * <p>
50 * The contents of the About field are in rtf.
51 * <p>
52 * \ is used as a continuation line.
53 *
54 * @see gnu.lgpl.License for license details.<br>
55 * The copyright to this program is held by it's authors.
56 * @author Mark Goodwin [mark at thorubio dot org]
57 * @author Joe Walker [joe at eireneh dot com]
58 * @author Jacky Cheung
59 * @author DM Smith [dmsmith555 at yahoo dot com]
60 */
61 /**
62 *
63 *
64 * @see gnu.lgpl.License for license details.<br>
65 * The copyright to this program is held by it's authors.
66 * @author DM Smith [dmsmith555 at yahoo dot com]
67 */
68 public final class SwordBookMetaData extends AbstractBookMetaData {
69 /**
70 * Loads a sword config from a given File.
71 *
72 * @param file
73 * @param internal
74 * @throws IOException
75 */
76 public SwordBookMetaData(File file, String internal, URI bookRootPath) throws IOException {
77 cet = new ConfigEntryTable(internal);
78 cet.load(file);
79
80 setLibrary(bookRootPath);
81 buildProperties();
82 }
83
84 /**
85 * Loads a sword config from a buffer.
86 *
87 * @param buffer
88 * @param internal
89 * @throws IOException
90 */
91 public SwordBookMetaData(byte[] buffer, String internal) throws IOException {
92 cet = new ConfigEntryTable(internal);
93 cet.load(buffer);
94 buildProperties();
95 }
96
97 /* (non-Javadoc)
98 * @see org.crosswire.jsword.book.BookMetaData#isQuestionable()
99 */
100 @Override
101 public boolean isQuestionable() {
102 return cet.isQuestionable();
103 }
104
105 /* (non-Javadoc)
106 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#isSupported()
107 */
108 @Override
109 public boolean isSupported() {
110 return cet.isSupported() && cet.getBookType().isSupported(this);
111 }
112
113 /* (non-Javadoc)
114 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#isEnciphered()
115 */
116 @Override
117 public boolean isEnciphered() {
118 return cet.isEnciphered();
119 }
120
121 /* (non-Javadoc)
122 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#isLocked()
123 */
124 @Override
125 public boolean isLocked() {
126 return cet.isLocked();
127 }
128
129 /* (non-Javadoc)
130 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#unlock(java.lang.String)
131 */
132 @Override
133 public boolean unlock(String unlockKey) {
134 return cet.unlock(unlockKey);
135 }
136
137 /* (non-Javadoc)
138 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#getUnlockKey()
139 */
140 @Override
141 public String getUnlockKey() {
142 return cet.getUnlockKey();
143 }
144
145 /* (non-Javadoc)
146 * @see org.crosswire.jsword.book.BookMetaData#getName()
147 */
148 public String getName() {
149 return (String) getProperty(ConfigEntryType.DESCRIPTION);
150 }
151
152 /**
153 * Returns the Charset of the book based on the encoding attribute
154 *
155 * @return the charset of the book.
156 */
157 public String getBookCharset() {
158 return ENCODING_JAVA.get(getProperty(ConfigEntryType.ENCODING));
159 }
160
161 /* (non-Javadoc)
162 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#getKeyType()
163 */
164 @Override
165 public KeyType getKeyType() {
166 BookType bookType = getBookType();
167 if (bookType == null) {
168 return null;
169 }
170 return bookType.getKeyType();
171 }
172
173 /**
174 * Returns the Book Type.
175 */
176 public BookType getBookType() {
177 return cet.getBookType();
178 }
179
180 /**
181 * Returns the sourceType.
182 */
183 public Filter getFilter() {
184 String sourcetype = (String) getProperty(ConfigEntryType.SOURCE_TYPE);
185 return FilterFactory.getFilter(sourcetype);
186 }
187
188 /**
189 * @return Returns the relative path of the book's conf.
190 */
191 public String getConfPath() {
192 return SwordConstants.DIR_CONF + '/' + getInitials().toLowerCase(Locale.ENGLISH) + SwordConstants.EXTENSION_CONF;
193 }
194
195 /* (non-Javadoc)
196 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#setLibrary(java.net.URI)
197 */
198 @Override
199 public void setLibrary(URI library) {
200 // Ignore it if it is not supported.
201 if (!isSupported()) {
202 return;
203 }
204
205 cet.add(ConfigEntryType.LIBRARY_URL, library.toString());
206 super.setLibrary(library);
207
208 // Previously, all DATA_PATH entries end in / to indicate dirs
209 // or not to indicate file prefixes.
210 // This is no longer true.
211 // Now we need to test the file/url to see if it exists and is a directory.
212 String datapath = (String) getProperty(ConfigEntryType.DATA_PATH);
213 int lastSlash = datapath.lastIndexOf('/');
214
215 // There were modules that did not have a valid datapath.
216 // This should not be necessary
217 if (lastSlash == -1) {
218 return;
219 }
220
221 // DataPath typically ends in a '/' to indicate a directory.
222 // If so remove it.
223 if (lastSlash == datapath.length() - 1) {
224 datapath = datapath.substring(0, lastSlash);
225 }
226
227 URI location = NetUtil.lengthenURI(library, datapath);
228 File bookDir = new File(location.getPath());
229 // For some modules, the last element of the DataPath
230 // is a prefix for file names.
231 if (!bookDir.isDirectory()) {
232 // Shorten it by one segment and test again.
233 lastSlash = datapath.lastIndexOf('/');
234 datapath = datapath.substring(0, lastSlash);
235 location = NetUtil.lengthenURI(library, datapath);
236 bookDir = new File(location.getPath());
237 if (!bookDir.isDirectory()) {
238 return;
239 }
240 }
241
242 cet.add(ConfigEntryType.LOCATION_URL, location.toString());
243 super.setLocation(location);
244 }
245
246 /* (non-Javadoc)
247 * @see org.crosswire.jsword.book.BookMetaData#getBookCategory()
248 */
249 public BookCategory getBookCategory() {
250 if (type == null) {
251 type = (BookCategory) getProperty(ConfigEntryType.CATEGORY);
252 if (type == BookCategory.OTHER) {
253 type = getBookType().getBookCategory();
254 }
255 }
256 return type;
257 }
258
259 /* (non-Javadoc)
260 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#toOSIS()
261 */
262 @Override
263 public Document toOSIS() {
264 return new Document(cet.toOSIS());
265 }
266
267 /* (non-Javadoc)
268 * @see org.crosswire.jsword.book.BookMetaData#getInitials()
269 */
270 public String getInitials() {
271 return (String) getProperty(ConfigEntryType.INITIALS);
272 }
273
274 /**
275 * Get the string value for the property or null if it is not defined. It is
276 * assumed that all properties gotten with this method are single line.
277 *
278 * @param entry
279 * the ConfigEntryType
280 * @return the property or null
281 */
282 public Object getProperty(ConfigEntryType entry) {
283 return cet.getValue(entry);
284 }
285
286 /* (non-Javadoc)
287 * @see org.crosswire.jsword.book.BookMetaData#isLeftToRight()
288 */
289 public boolean isLeftToRight() {
290 // This should return the dominate direction of the text, if it is BiDi,
291 // then we have to guess.
292 String dir = (String) getProperty(ConfigEntryType.DIRECTION);
293 if (ConfigEntryType.DIRECTION_BIDI.equalsIgnoreCase(dir)) {
294 // When BiDi, return the dominate direction based upon the Book's
295 // Language not Direction
296 Language lang = (Language) getProperty(ConfigEntryType.LANG);
297 return lang.isLeftToRight();
298 }
299
300 return ConfigEntryType.DIRECTION_LTOR.equalsIgnoreCase(dir);
301 }
302
303 /* (non-Javadoc)
304 * @see org.crosswire.jsword.book.basic.AbstractBookMetaData#hasFeature(org.crosswire.jsword.book.FeatureType)
305 */
306 @Override
307 public boolean hasFeature(FeatureType feature) {
308 if (cet.match(ConfigEntryType.FEATURE, feature.toString())) {
309 return true;
310 }
311 // Many "features" are GlobalOptionFilters, which in the Sword C++ API
312 // indicate a class to use for filtering.
313 // These mostly have the source type prepended to the feature
314 StringBuilder buffer = new StringBuilder((String) getProperty(ConfigEntryType.SOURCE_TYPE));
315 buffer.append(feature);
316 if (cet.match(ConfigEntryType.GLOBAL_OPTION_FILTER, buffer.toString())) {
317 return true;
318 }
319 // But some do not
320 return cet.match(ConfigEntryType.GLOBAL_OPTION_FILTER, feature.toString());
321 }
322
323 private void buildProperties() {
324 // merge entries into properties file
325 for (ConfigEntryType key : cet.getKeys()) {
326 Object value = cet.getValue(key);
327 // value is null if the config entry was rejected.
328 if (value == null) {
329 continue;
330 }
331 if (value instanceof List<?>) {
332 List<String> list = (List<String>) value;
333 StringBuilder combined = new StringBuilder();
334 boolean appendSeparator = false;
335 for (String element : list) {
336 if (appendSeparator) {
337 combined.append('\n');
338 }
339 combined.append(element);
340 appendSeparator = true;
341 }
342
343 value = combined.toString();
344 }
345
346 putProperty(key.toString(), value);
347 }
348 }
349
350 /**
351 * Sword only recognizes two encodings for its modules: UTF-8 and LATIN1
352 * Sword uses MS Windows cp1252 for Latin 1 not the standard. Arrgh! The
353 * language strings need to be converted to Java charsets
354 */
355 private static final PropertyMap ENCODING_JAVA = new PropertyMap();
356 static {
357 ENCODING_JAVA.put("Latin-1", "WINDOWS-1252");
358 ENCODING_JAVA.put("UTF-8", "UTF-8");
359 }
360
361 private ConfigEntryTable cet;
362 private BookCategory type;
363 }
364