1
20 package org.crosswire.jsword.book.sword;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.PrintWriter;
25 import java.io.Writer;
26 import java.net.URI;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.regex.Pattern;
38
39 import org.crosswire.common.util.Filter;
40 import org.crosswire.common.util.IOUtil;
41 import org.crosswire.common.util.IniSection;
42 import org.crosswire.common.util.Language;
43 import org.crosswire.common.util.NetUtil;
44 import org.crosswire.common.util.PropertyMap;
45 import org.crosswire.common.xml.XMLUtil;
46 import org.crosswire.jsword.JSMsg;
47 import org.crosswire.jsword.book.BookCategory;
48 import org.crosswire.jsword.book.BookException;
49 import org.crosswire.jsword.book.FeatureType;
50 import org.crosswire.jsword.book.KeyType;
51 import org.crosswire.jsword.book.MetaDataLocator;
52 import org.crosswire.jsword.book.OSISUtil;
53 import org.crosswire.jsword.book.basic.AbstractBookMetaData;
54 import org.crosswire.jsword.book.filter.SourceFilter;
55 import org.crosswire.jsword.book.filter.SourceFilterFactory;
56 import org.crosswire.jsword.versification.system.Versifications;
57 import org.jdom2.Document;
58 import org.jdom2.Element;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62
97 public final class SwordBookMetaData extends AbstractBookMetaData {
98 public static final String KEY_ABBREVIATION = "Abbreviation";
108 public static final String KEY_ABOUT = "About";
109 public static final String KEY_BLOCK_COUNT = "BlockCount";
110 public static final String KEY_BLOCK_TYPE = "BlockType";
111 public static final String KEY_CASE_SENSITIVE_KEYS = "CaseSensitiveKeys";
112 public static final String KEY_CIPHER_KEY = "CipherKey";
113 public static final String KEY_COMPRESS_TYPE = "CompressType";
114 public static final String KEY_COPYRIGHT = "Copyright";
115 public static final String KEY_COPYRIGHT_CONTACT_ADDRESS = "CopyrightContactAddress";
116 public static final String KEY_COPYRIGHT_CONTACT_EMAIL = "CopyrightContactEmail";
117 public static final String KEY_COPYRIGHT_CONTACT_NAME = "CopyrightContactName";
118 public static final String KEY_COPYRIGHT_CONTACT_NOTES = "CopyrightContactNotes";
119 public static final String KEY_COPYRIGHT_DATE = "CopyrightDate";
120 public static final String KEY_COPYRIGHT_HOLDER = "CopyrightHolder";
121 public static final String KEY_COPYRIGHT_NOTES = "CopyrightNotes";
122 public static final String KEY_DATA_PATH = "DataPath";
123 public static final String KEY_DESCRIPTION = "Description";
124 public static final String KEY_DIRECTION = "Direction";
125 public static final String KEY_DISPLAY_LEVEL = "DisplayLevel";
126 public static final String KEY_DISTRIBUTION_LICENSE = "DistributionLicense";
127 public static final String KEY_DISTRIBUTION_NOTES = "DistributionNotes";
128 public static final String KEY_DISTRIBUTION_SOURCE = "DistributionSource";
129 public static final String KEY_ENCODING = "Encoding";
130 public static final String KEY_FEATURE = "Feature";
131 public static final String KEY_GLOBAL_OPTION_FILTER = "GlobalOptionFilter";
132 public static final String KEY_SIGLUM1 = "Siglum1";
133 public static final String KEY_SIGLUM2 = "Siglum2";
134 public static final String KEY_SIGLUM3 = "Siglum3";
135 public static final String KEY_SIGLUM4 = "Siglum4";
136 public static final String KEY_SIGLUM5 = "Siglum5";
137 public static final String KEY_GLOSSARY_FROM = "GlossaryFrom";
138 public static final String KEY_GLOSSARY_TO = "GlossaryTo";
139 public static final String KEY_HISTORY = "History";
140 public static final String KEY_INSTALL_SIZE = "InstallSize";
141 public static final String KEY_KEY_TYPE = "KeyType";
142 public static final String KEY_LCSH = "LCSH";
143 public static final String KEY_LOCAL_STRIP_FILTER = "LocalStripFilter";
144 public static final String KEY_MINIMUM_VERSION = "MinimumVersion";
145 public static final String KEY_MOD_DRV = "ModDrv";
146 public static final String KEY_OBSOLETES = "Obsoletes";
147 public static final String KEY_OSIS_Q_TO_TICK = "OSISqToTick";
148 public static final String KEY_OSIS_VERSION = "OSISVersion";
149 public static final String KEY_PREFERRED_CSS_XHTML = "PreferredCSSXHTML";
150 public static final String KEY_SEARCH_OPTION = "SearchOption";
151 public static final String KEY_SHORT_COPYRIGHT = "ShortCopyright";
152 public static final String KEY_SHORT_PROMO = "ShortPromo";
153 public static final String KEY_SOURCE_TYPE = "SourceType";
154 public static final String KEY_STRONGS_PADDING = "StrongsPadding";
155 public static final String KEY_SWORD_VERSION_DATE = "SwordVersionDate";
156 public static final String KEY_TEXT_SOURCE = "TextSource";
157 public static final String KEY_UNLOCK_URL = "UnlockURL";
158 public static final String KEY_VERSION = "Version";
159
160 public static final Map<String, String> DEFAULTS;
162 static {
163 Map<String, String> tempMap = new HashMap<String, String>();
164 tempMap.put(KEY_COMPRESS_TYPE, "LZSS");
165 tempMap.put(KEY_BLOCK_TYPE, "CHAPTER");
166 tempMap.put(KEY_BLOCK_COUNT, "200");
167 tempMap.put(KEY_KEY_TYPE, "TreeKey");
168 tempMap.put(KEY_VERSIFICATION, "KJV");
169 tempMap.put(KEY_DIRECTION, "LtoR");
170 tempMap.put(KEY_SOURCE_TYPE, "Plaintext");
171 tempMap.put(KEY_ENCODING, "Latin-1");
172 tempMap.put(KEY_DISPLAY_LEVEL, "1");
173 tempMap.put(KEY_OSIS_Q_TO_TICK, "true");
174 tempMap.put(KEY_VERSION, "1.0");
175 tempMap.put(KEY_MINIMUM_VERSION, "1.5.1a");
176 tempMap.put(KEY_CATEGORY, "Other");
177 tempMap.put(KEY_LANG, "en");
178 tempMap.put(KEY_DISTRIBUTION_LICENSE, "Public Domain");
179 tempMap.put(KEY_CASE_SENSITIVE_KEYS, "false");
180 tempMap.put(KEY_STRONGS_PADDING, "true");
181 DEFAULTS = Collections.unmodifiableMap(tempMap);
182 }
183
184
195 public SwordBookMetaData(File file, URI bookRootPath) throws IOException, BookException {
196 this.installed = true;
197 this.configFile = file;
198 this.bookConf = file.getName(); setLibrary(bookRootPath);
200
201 this.configAll = new IniSection();
202 this.filtered = true; reload(keyKeepers);
204 }
205
206
214 public SwordBookMetaData(byte[] buffer, String bookConf) throws IOException, BookException {
215 this.installed = false;
216 this.bookConf = bookConf; this.supported = true;
218 this.configAll = new IniSection();
219 this.filtered = true; loadBuffer(buffer, keyKeepers);
221 adjustConfig();
222 report(configAll);
223 }
224
225
228 @Override
229 public void reload() throws BookException {
230 reload(null);
231 }
232
233 public void reload(Filter<String> keepers) throws BookException {
234 if (!filtered && keepers == null) {
238 return;
239 }
240 this.supported = true;
241 if (configJSword != null) {
242 configJSword.clear();
243 }
244 if (configFrontend != null) {
245 configFrontend.clear();
246 }
247 try {
248 if (installed) {
249 loadFile(keepers);
250 } else {
251 byte[] buffer = IOUtil.getZipEntry(bookConf);
252 loadBuffer(buffer, keepers);
253 }
254 adjustConfig();
255 report(configAll);
256
257 this.configJSword = addConfig(MetaDataLocator.JSWORD);
258 this.configFrontend = addConfig(MetaDataLocator.FRONTEND);
259 } catch (IOException ex) {
260 throw new BookException("unable to load conf", ex);
261 }
262 }
263
264
267 @Override
268 public boolean isQuestionable() {
269 return questionable;
271 }
272
273
276 @Override
277 public boolean isSupported() {
278 return supported;
280 }
281
282
285 @Override
286 public boolean isEnciphered() {
287 String cipher = getProperty(KEY_CIPHER_KEY);
288 return cipher != null;
289 }
290
291
294 @Override
295 public boolean isLocked() {
296 String cipher = getProperty(KEY_CIPHER_KEY);
298 return cipher != null && cipher.length() == 0;
299 }
300
301
304 @Override
305 public boolean unlock(String unlockKey) {
306 putProperty(KEY_CIPHER_KEY, unlockKey, false);
308 return true;
309 }
310
311
316 @Override
317 public String getUnlockKey() {
318 return getProperty(KEY_CIPHER_KEY);
319 }
320
321
326 public String getName() {
327 return getProperty(KEY_DESCRIPTION);
328 }
329
330
333 public String getBookCharset() {
334 return ENCODING_JAVA.get(getProperty(KEY_ENCODING));
335 }
336
337
341 @Override
342 public KeyType getKeyType() {
343 BookType bt = getBookType();
344 if (bt == null) {
345 return null;
346 }
347 return bt.getKeyType();
348 }
349
350
353 public BookType getBookType() {
354 return bookType;
355 }
356
357
360 public SourceFilter getFilter() {
361 String sourcetype = getProperty(KEY_SOURCE_TYPE);
362 return SourceFilterFactory.getFilter(sourcetype);
363 }
364
365
371 public File getConfigFile() {
372 return configFile;
373 }
374
375
379 public BookCategory getBookCategory() {
380 if (bookCat == null) {
381 bookCat = (BookCategory) getValue(KEY_CATEGORY);
382 if (bookCat == BookCategory.OTHER) {
383 BookType bt = getBookType();
384 if (bt == null) {
385 return bookCat;
386 }
387 bookCat = bt.getBookCategory();
388 }
389 }
390 return bookCat;
391 }
392
393
397 @Override
398 public Document toOSIS() {
399 List<String> knownKeys = new ArrayList(configAll.getKeys());
400 OSISUtil.OSISFactory factory = OSISUtil.factory();
401 Element table = factory.createTable();
402 Element row = toRow(factory, "Initials", getInitials());
403 table.addContent(row);
404 for (String key : OSIS_INFO) {
406 knownKeys.remove(key);
407 row = toRow(factory, key);
408 if (row != null) {
409 table.addContent(row);
410 }
411 }
412 List<String> hide = Arrays.asList(HIDDEN);
415 for (String key : knownKeys) {
416 if (hide.contains(key)) {
417 continue;
418 }
419 row = toRow(factory, key);
420 if (row != null) {
421 table.addContent(row);
422 }
423 }
424 return new Document(table);
425 }
426
427 public static void normalize(Writer out, final IniSection config, final String[] order) {
428 PrintWriter writer = null;
429 if (out instanceof PrintWriter) {
430 writer = (PrintWriter) out;
431 } else {
432 writer = new PrintWriter(out);
433 }
434 IniSection copy = new IniSection(config);
435 adjustHistory(copy);
437
438 List<String> knownKeys = new ArrayList(copy.getKeys());
439 writer.print("[");
440 writer.print(copy.getName());
441 writer.print("]");
442 writer.println();
443
444 for (String key : order) {
446 knownKeys.remove(key);
447 if (!copy.containsKey(key)) {
448 continue;
449 }
450 Collection<String> values = copy.getValues(key);
451 Iterator<String> iter = values.iterator();
452 String value;
453 while (iter.hasNext()) {
454 value = iter.next();
455 String newKey = key;
456 if (KEY_HISTORY.equalsIgnoreCase(key)) {
458 int pos = value.indexOf(' ');
459 newKey += '_' + value.substring(0, pos);
460 value = value.substring(pos + 1);
461 }
462 writer.print(newKey);
463 writer.print("=");
464 writer.print(value.replaceAll("\n", " \\\\\n"));
465 writer.println();
466 }
467 }
468
469 Iterator<String> keys = knownKeys.iterator();
470 while (keys.hasNext()) {
471 String key = keys.next();
472 Collection<String> values = copy.getValues(key);
473 Iterator<String> iter = values.iterator();
474 String value;
475 while (iter.hasNext()) {
476 value = iter.next();
477 writer.print(key);
478 writer.print("=");
479 writer.print(value.replaceAll("\n", " \\\\\n"));
480 writer.println();
481 }
482 }
483
484 writer.flush();
485 }
486
487
490 public String getInitials() {
491 return configAll.getName();
492 }
493
494
498 String getInternalName() {
499 return configAll.getName();
500 }
501
502
505 public String getAbbreviation() {
506 String abbreviation = getProperty(KEY_ABBREVIATION);
507 if (abbreviation != null && abbreviation.length() > 0) {
508 return abbreviation;
509 }
510 return getInitials();
511 }
512
513
516 public boolean isLeftToRight() {
517 String dir = getProperty(KEY_DIRECTION);
520 if (ConfigEntryType.DIRECTION_BIDI.equalsIgnoreCase(dir)) {
521 Language lang = getLanguage();
524 return lang.isLeftToRight();
525 }
526
527 return ConfigEntryType.DIRECTION_LTOR.equalsIgnoreCase(dir);
528 }
529
530
533 @Override
534 public boolean hasFeature(FeatureType feature) {
535 String name = feature.toString();
536 if (configAll.containsValue(KEY_FEATURE, name)) {
539 return true;
540 }
541 StringBuilder buffer = new StringBuilder(getProperty(KEY_SOURCE_TYPE));
545 buffer.append(name);
546 if (configAll.containsValue(KEY_GLOBAL_OPTION_FILTER, buffer.toString())) {
547 return true;
548 }
549
550 String alias = feature.getAlias();
552 buffer.setLength(0);
553 buffer.append(getProperty(KEY_SOURCE_TYPE));
554 buffer.append(alias);
555
556 return configAll.containsValue(KEY_GLOBAL_OPTION_FILTER, name) || configAll.containsValue(KEY_GLOBAL_OPTION_FILTER, buffer.toString());
558
559 }
560
561
564 public Set<String> getPropertyKeys() {
565 return null;
566 }
567
568
571 public String getProperty(String key) {
572 if (KEY_LANGUAGE.equals(key)) {
573 return getLanguage().getName();
574 }
575 return configAll.get(key, DEFAULTS.get(key));
576 }
577
578
581 public void setProperty(String key, String value) {
582 configAll.replace(key, value);
583 }
584
585
588 public void putProperty(String key, String value, boolean forFrontend) {
589 MetaDataLocator mdl = forFrontend ? MetaDataLocator.FRONTEND : MetaDataLocator.JSWORD;
590 putProperty(key, value, mdl);
591 }
592
593
600 public void putProperty(String key, String value, MetaDataLocator metaDataLocator) {
601 setProperty(key, value);
603
604 if (!installed) {
606 return;
607 }
608
609 File writeLocation = metaDataLocator.getWriteLocation();
610 if (writeLocation == null) {
612 return;
613 }
614 IniSection config = null;
617 switch (metaDataLocator) {
618 case FRONTEND:
619 if (this.configFrontend == null) {
620 this.configFrontend = new IniSection(configAll.getName());
621 }
622 config = this.configFrontend;
623 break;
624 case JSWORD:
625 if (this.configJSword == null) {
626 this.configJSword = new IniSection(configAll.getName());
627 }
628 config = this.configJSword;
629 break;
630 case TRANSIENT:
631 default:
632 break;
633 }
634
635 if (config != null) {
636 config.replace(key, value);
637 try {
638 config.save(new File(writeLocation, bookConf), getBookCharset());
639 } catch (IOException ex) {
640 LOGGER.error("Unable to save {}={}: conf file for [{}]; error={}", key, value, configAll.getName(), ex);
641 }
642 }
643 }
644
645
651 public static void setPartialLoading(boolean partial) {
652
653 if (partial != partialLoading) {
654 if (partial) {
655 keyKeepers = new KeyFilter(REQUIRED);
656 } else {
657 keyKeepers = null;
658 }
659 }
660 partialLoading = partial;
661 }
662
663
670 private void loadFile(Filter<String> keepers) throws IOException {
671 filtered = keepers != null;
672
673 configAll.clear();
674 configAll.load(configFile, ENCODING_UTF8, keepers);
675 String encoding = configAll.get(KEY_ENCODING);
676 if (!ENCODING_UTF8.equalsIgnoreCase(encoding)) {
677 configAll.clear();
678 configAll.load(configFile, ENCODING_LATIN1, keepers);
679 }
680 }
681
682
689 private void loadBuffer(byte[] buffer, Filter<String> keepers) throws IOException {
690 filtered = keepers != null;
691
692 configAll.clear();
693 configAll.load(buffer, ENCODING_UTF8, keepers);
694 String encoding = configAll.get(KEY_ENCODING);
695 if (!ENCODING_UTF8.equalsIgnoreCase(encoding)) {
696 configAll.clear();
697 configAll.load(buffer, ENCODING_LATIN1, keepers);
698 }
699 }
700
701 private IniSection addConfig(MetaDataLocator locator) {
702 File conf = new File(locator.getWriteLocation(), bookConf);
704 if (!conf.exists()) {
705 conf = new File(locator.getReadLocation(), bookConf);
706 }
707
708 if (conf.exists()) {
709 String encoding = getProperty(KEY_ENCODING);
711 try {
712 IniSection config = new IniSection();
713 config.load(conf, encoding);
714 mergeConfig(config);
715 return config;
716 } catch (IOException e) {
717 LOGGER.error("Unable to load conf {}:{}", conf, e);
718 }
719 }
720
721 return null;
722 }
723
724 private void mergeConfig(IniSection config) {
725 for (String key : config.getKeys()) {
726 ConfigEntryType type = ConfigEntryType.fromString(key);
727 for (String value : config.getValues(key)) {
728 if (type != null && type.mayRepeat()) {
729 if (!configAll.containsValue(key, value)) {
730 configAll.add(key, value);
731 }
732 } else {
733 setProperty(key, value);
734 }
735 }
736 }
737 }
738
739
746 private Object getValue(String key) {
747 ConfigEntryType type = ConfigEntryType.fromString(key);
748 String ce = getProperty(key);
749 if (type == null) {
750 return ce;
751 }
752
753 return ce == null ? null : type.convert(ce);
754 }
755
756 private Element toRow(OSISUtil.OSISFactory factory, String key, String value) {
757 Element nameEle = toKeyCell(factory, key);
758
759 Element valueElement = factory.createCell();
760 valueElement.addContent(value);
761
762 Element rowEle = factory.createRow();
764 rowEle.addContent(nameEle);
765 rowEle.addContent(valueElement);
766 return rowEle;
767 }
768
769 private Element toRow(OSISUtil.OSISFactory factory, String key) {
770 int size = configAll.size(key);
771 if (size == 0) {
772 return null;
773 }
774
775 ConfigEntryType type = ConfigEntryType.fromString(key);
777 Element nameEle = toKeyCell(factory, key);
778
779 Element valueElement = factory.createCell();
780 for (int j = 0; j < size; j++) {
781 if (j > 0) {
782 valueElement.addContent(factory.createLB());
783 }
784
785 String text = configAll.get(key, j);
786 if (type != null && !type.isText() && type.isAllowed(text)) {
787 text = type.convert(text).toString();
788 }
789 text = XMLUtil.escape(text);
790 if (type != null && type.allowsRTF()) {
791 valueElement.addContent(OSISUtil.rtfToOsis(text));
792 } else {
793 valueElement.addContent(text);
794 }
795 }
796
797 Element rowEle = factory.createRow();
799 rowEle.addContent(nameEle);
800 rowEle.addContent(valueElement);
801
802 return rowEle;
803 }
804
805 private Element toKeyCell(OSISUtil.OSISFactory factory, String key) {
806 Element nameEle = factory.createCell();
807 Element hiEle = factory.createHI();
808 hiEle.setAttribute(OSISUtil.OSIS_ATTR_TYPE, OSISUtil.HI_BOLD);
809 nameEle.addContent(hiEle);
810 hiEle.addContent(key);
812 return nameEle;
813 }
814
815 private void adjustConfig() throws BookException {
816 adjustLocation();
817 adjustLanguage();
818 adjustBookType();
819 adjustName();
820 adjustHistory(configAll);
821 }
822
823 private void adjustLanguage() {
824 String lang = getProperty(KEY_LANG);
825 testLanguage(KEY_LANG, lang);
826
827 String langFrom = configAll.get(KEY_GLOSSARY_FROM);
828 String langTo = configAll.get(KEY_GLOSSARY_TO);
829
830 if (langFrom != null || langTo != null) {
832 if (langFrom == null) {
833 langFrom = lang;
834 setProperty(KEY_GLOSSARY_FROM, langFrom);
835 LOGGER.warn("Missing data for [{}]. Assuming {}={}", configAll.getName(), KEY_GLOSSARY_FROM, langFrom);
836 }
837 testLanguage(KEY_GLOSSARY_FROM, langFrom);
838
839 if (langTo == null) {
840 langTo = Language.DEFAULT_LANG.getGivenSpecification();
841 setProperty(KEY_GLOSSARY_TO, langTo);
842 LOGGER.warn("Missing data for [{}]. Assuming {}={}", configAll.getName(), KEY_GLOSSARY_TO, langTo);
843 }
844 testLanguage(KEY_GLOSSARY_TO, langTo);
845
846 if (!langFrom.equals(lang) && !langTo.equals(lang)) {
848 LOGGER.error("Data error in [{}]. Neither {} or {} match {}", configAll.getName(), KEY_GLOSSARY_FROM, KEY_GLOSSARY_TO, KEY_LANG);
849 }
850 }
851
852 setLanguage((Language) getValue(KEY_LANG));
853 }
854
855 private void testLanguage(String key, String lang) {
856 Language language = new Language(lang);
857 if (!language.isValidLanguage()) {
858 LOGGER.warn("Unknown language [{}]{}={}", configAll.getName(), key, lang);
859 }
860 }
861
862 private void adjustBookType() {
863 BookCategory focusedCategory = (BookCategory) getValue(KEY_CATEGORY);
866 questionable = focusedCategory == BookCategory.QUESTIONABLE;
867
868 String modTypeName = getProperty(KEY_MOD_DRV);
869 if (modTypeName == null) {
870 LOGGER.error("Book not supported: malformed conf file for [{}] no {} found.", configAll.getName(), KEY_MOD_DRV);
871 supported = false;
872 return;
873 }
874
875 String v11n = getProperty(KEY_VERSIFICATION);
876 if (!Versifications.instance().isDefined(v11n)) {
877 LOGGER.error("Book not supported: Unknown versification for [{}]{}={}.", configAll.getName(), KEY_VERSIFICATION, v11n);
878 supported = false;
879 return;
880 }
881
882 bookType = BookType.fromString(modTypeName);
883 if (bookType == null) {
884 LOGGER.error("Book not supported: malformed conf file for [{}] no book type found", configAll.getName());
885 supported = false;
886 return;
887 }
888
889 if (focusedCategory == BookCategory.OTHER) {
892 focusedCategory = bookType.getBookCategory();
893 }
894
895 setProperty(KEY_CATEGORY, focusedCategory.getName());
896 }
897
898 private void adjustName() {
899 if (configAll.get(KEY_DESCRIPTION) == null) {
901 LOGGER.error("Malformed conf file: missing [{}]{}=. Using {}", configAll.getName(), KEY_DESCRIPTION, configAll.getName());
902 setProperty(KEY_DESCRIPTION, configAll.getName());
903 }
904 }
905
906
908 private void adjustLocation() throws BookException {
909
910 URI library = getLibrary();
911 if (library == null) {
912 return;
913 }
914
915 String datapath = getProperty(KEY_DATA_PATH);
921 int lastSlash = datapath.lastIndexOf('/');
922
923 if (lastSlash == -1) {
926 return;
927 }
928
929 boolean isDirectoryPath = false;
932 if (lastSlash == datapath.length() - 1) {
933 isDirectoryPath = true;
934 datapath = datapath.substring(0, lastSlash);
935 }
936
937 URI location = NetUtil.lengthenURI(library, datapath);
938 File bookDir = new File(location.getPath());
939 if (!bookDir.isDirectory()) {
942 if (isDirectoryPath) {
943 throw new BookException(JSMsg.gettext("The book {0} is missing its data files", configAll.getName()));
945 }
946
947 if (!new File(location.getPath() + ".dat").exists()) {
951 throw new BookException(JSMsg.gettext("The book {0} is missing its data files", configAll.getName()));
954 }
955
956 lastSlash = datapath.lastIndexOf('/');
959 datapath = datapath.substring(0, lastSlash);
960 location = NetUtil.lengthenURI(library, datapath);
961 }
962
963 setLocation(location);
964 }
965
966 private static void adjustHistory(IniSection config) {
970 List<String> keys = new ArrayList(config.getKeys());
974 for (String key : keys) {
975 String value = config.get(key);
976 ConfigEntryType type = ConfigEntryType.fromString(key);
977 if (ConfigEntryType.HISTORY.equals(type)) {
978 config.remove(key);
979 int pos = key.indexOf('_');
980 value = key.substring(pos + 1) + ' ' + value;
981 config.add(KEY_HISTORY, value);
982 }
983 }
984 }
985
986 public static void report(final IniSection config) {
987 StringBuilder buf = new StringBuilder(config.report());
988 for (String key : config.getKeys()) {
989 ConfigEntryType type = ConfigEntryType.fromString(key);
990
991 if (type == null) {
992 if (key.contains("_")) {
993 String baseKey = key.substring(0, key.indexOf('_'));
994 type = ConfigEntryType.fromString(baseKey);
995 }
996 }
997
998
999 int count = config.size(key);
1000 for (int i = 0; i < count; i++) {
1001 String value = config.get(key, i);
1002
1003 if (type == null) {
1005 buf.append("Unknown entry: ").append(key).append(" = ").append(value).append('\n');
1006 continue;
1007 }
1008
1009 if (value.length() == 0 && type != ConfigEntryType.CIPHER_KEY) {
1011 buf.append("Unexpected empty entry: ").append(key).append(" = ").append(value).append('\n');
1012 continue;
1013 }
1014
1015 value = type.filter(value);
1017
1018 if (!type.allowsRTF() && RTF_PATTERN.matcher(value).find()) {
1020 buf.append("Unexpected RTF: ").append(key).append(" = ").append(value).append('\n');
1021 }
1022
1023 if (!type.allowsHTML() && HTML_PATTERN.matcher(value).find()) {
1024 buf.append("Unexpected HTML: ").append(key).append(" = ").append(value).append('\n');
1025 }
1026
1027 if (!type.isAllowed(value)) {
1028 buf.append("Unknown config value: ").append(key).append(" = ").append(value).append('\n');
1029 }
1030
1031 if (count > 1 && !type.mayRepeat()) {
1032 buf.append("Unexpected repeated config key: ").append(key).append(" = ").append(value).append('\n');
1033 }
1034 }
1035 }
1036 if (buf.length() > 0) {
1037 LOGGER.info("Conf report for [{}]\n{}", config.getName(), buf.toString());
1038 }
1039 }
1040
1041
1044 private boolean installed;
1045
1046
1050 private boolean filtered;
1051
1052
1055 private String bookConf;
1056
1057
1061 private IniSection configAll;
1062
1063
1066 private IniSection configJSword;
1067
1068
1071 private IniSection configFrontend;
1072
1073
1076 private boolean supported;
1077
1078
1081 private BookCategory bookCat;
1082
1083
1086 private BookType bookType;
1087
1088
1091 private boolean questionable;
1092
1093
1097 private File configFile;
1098
1099
1102 private static final String[] REQUIRED = {
1103 KEY_ABBREVIATION,
1104 KEY_DESCRIPTION,
1105 KEY_LANG,
1106 KEY_CATEGORY,
1107 KEY_VERSION,
1108 KEY_FEATURE,
1109 KEY_GLOBAL_OPTION_FILTER,
1110 KEY_SIGLUM1,
1111 KEY_SIGLUM2,
1112 KEY_SIGLUM3,
1113 KEY_SIGLUM4,
1114 KEY_SIGLUM5,
1115 KEY_FONT,
1116 KEY_DATA_PATH,
1117 KEY_MOD_DRV,
1118 KEY_SOURCE_TYPE,
1119 KEY_BLOCK_TYPE,
1120 KEY_BLOCK_COUNT,
1121 KEY_COMPRESS_TYPE,
1122 KEY_ENCODING,
1123 KEY_DIRECTION,
1124 KEY_KEY_TYPE,
1125 KEY_DISPLAY_LEVEL,
1126 KEY_VERSIFICATION,
1127 KEY_CASE_SENSITIVE_KEYS,
1128 KEY_LOCAL_STRIP_FILTER,
1129 KEY_PREFERRED_CSS_XHTML,
1130 KEY_STRONGS_PADDING,
1131 KEY_SEARCH_OPTION,
1132 KEY_INSTALL_SIZE,
1133 KEY_SCOPE,
1134 KEY_BOOKLIST,
1135 KEY_CIPHER_KEY
1136 };
1137
1138
1142 private static final class KeyFilter implements Filter<String> {
1143
1147 KeyFilter(String[] keepers) {
1148 this.keepers = new HashSet();
1149
1150 for (String key : keepers) {
1152 this.keepers.add(key);
1153 }
1154 }
1155 public boolean test(String key) {
1156 return keepers.contains(key);
1157 }
1158 private Set keepers;
1159 }
1160
1161 private static boolean partialLoading;
1162
1163 private static Filter keyKeepers;
1164
1165 private static final String[] OSIS_INFO = {
1166 KEY_ABBREVIATION,
1167 KEY_DESCRIPTION,
1168 KEY_LANG,
1169 KEY_CATEGORY,
1170 KEY_LCSH,
1171 KEY_SWORD_VERSION_DATE,
1172 KEY_VERSION,
1173 KEY_HISTORY,
1174 KEY_OBSOLETES,
1175 KEY_GLOSSARY_FROM,
1176 KEY_GLOSSARY_TO,
1177 KEY_ABOUT,
1178 KEY_SHORT_PROMO,
1179 KEY_DISTRIBUTION_LICENSE,
1180 KEY_DISTRIBUTION_NOTES,
1181 KEY_DISTRIBUTION_SOURCE,
1182 KEY_SHORT_COPYRIGHT,
1183 KEY_COPYRIGHT,
1184 KEY_COPYRIGHT_DATE,
1185 KEY_COPYRIGHT_HOLDER,
1186 KEY_COPYRIGHT_CONTACT_NAME,
1187 KEY_COPYRIGHT_CONTACT_ADDRESS,
1188 KEY_COPYRIGHT_CONTACT_EMAIL,
1189 KEY_COPYRIGHT_CONTACT_NOTES,
1190 KEY_COPYRIGHT_NOTES,
1191 KEY_TEXT_SOURCE,
1192 KEY_FEATURE,
1193 KEY_GLOBAL_OPTION_FILTER,
1194 KEY_SIGLUM1,
1195 KEY_SIGLUM2,
1196 KEY_SIGLUM3,
1197 KEY_SIGLUM4,
1198 KEY_SIGLUM5,
1199 KEY_FONT,
1200 KEY_DATA_PATH,
1201 KEY_MOD_DRV,
1202 KEY_SOURCE_TYPE,
1203 KEY_BLOCK_TYPE,
1204 KEY_BLOCK_COUNT,
1205 KEY_COMPRESS_TYPE,
1206 KEY_ENCODING,
1207 KEY_MINIMUM_VERSION,
1208 KEY_OSIS_VERSION,
1209 KEY_OSIS_Q_TO_TICK,
1210 KEY_DIRECTION,
1211 KEY_KEY_TYPE,
1212 KEY_DISPLAY_LEVEL,
1213 KEY_VERSIFICATION,
1214 KEY_CASE_SENSITIVE_KEYS,
1215 KEY_LOCAL_STRIP_FILTER,
1216 KEY_PREFERRED_CSS_XHTML,
1217 KEY_STRONGS_PADDING,
1218 KEY_SEARCH_OPTION,
1219 KEY_INSTALL_SIZE,
1220 KEY_SCOPE,
1221 KEY_BOOKLIST
1222 };
1223
1224 private static final String[] HIDDEN = {
1225 KEY_CIPHER_KEY,
1226 KEY_LANGUAGE
1227 };
1228
1229 private static final Pattern RTF_PATTERN = Pattern.compile("\\\\pard|\\\\pa[er]|\\\\qc|\\\\[bi]|\\\\u-?[0-9]{4,6}+");
1230 private static final Pattern HTML_PATTERN = Pattern.compile("(<[a-zA-Z]|[a-zA-Z]>)");
1231
1232
1237 private static final String ENCODING_UTF8 = "UTF-8";
1238 private static final String ENCODING_LATIN1 = "WINDOWS-1252";
1239 private static final PropertyMap ENCODING_JAVA = new PropertyMap();
1240 static {
1241 ENCODING_JAVA.put("Latin-1", ENCODING_LATIN1);
1242 ENCODING_JAVA.put("UTF-8", ENCODING_UTF8);
1243 }
1244
1245
1248 private static final Logger LOGGER = LoggerFactory.getLogger(SwordBookMetaData.class);
1249
1250}
1251