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: AbstractBackend.java 2099 2011-03-07 17:13:00Z 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  
28  import org.crosswire.common.activate.Activatable;
29  import org.crosswire.common.crypt.Sapphire;
30  import org.crosswire.common.util.NetUtil;
31  import org.crosswire.jsword.JSOtherMsg;
32  import org.crosswire.jsword.book.BookException;
33  import org.crosswire.jsword.passage.Key;
34  
35  /**
36   * A generic way to read data from disk for later formatting.
37   * 
38   * @see gnu.lgpl.License for license details.<br>
39   *      The copyright to this program is held by it's authors.
40   * @author Joe Walker [joe at eireneh dot com]
41   * @author DM Smith [dmsmith555 at yahoo dot com]
42   */
43  public abstract class AbstractBackend implements Activatable {
44      /**
45       * Default constructor for the sake of serialization.
46       */
47      /* protected */public AbstractBackend() {
48      }
49  
50      /**
51       * Construct a minimal backend
52       * 
53       * @param sbmd
54       */
55      public AbstractBackend(SwordBookMetaData sbmd) {
56          bmd = sbmd;
57      }
58  
59      /**
60       * @return Returns the Sword BookMetaData.
61       */
62      public SwordBookMetaData getBookMetaData() {
63          return bmd;
64      }
65  
66      /**
67       * Decipher the data in place, if it is enciphered and there is a key to
68       * unlock it.
69       * 
70       * @param data
71       *            the data to unlock
72       */
73      public void decipher(byte[] data) {
74          String cipherKeyString = (String) getBookMetaData().getProperty(ConfigEntryType.CIPHER_KEY);
75          if (cipherKeyString != null) {
76              Sapphire cipherEngine = new Sapphire(cipherKeyString.getBytes());
77              for (int i = 0; i < data.length; i++) {
78                  data[i] = cipherEngine.cipher(data[i]);
79              }
80              // destroy any evidence!
81              cipherEngine.burn();
82          }
83      }
84  
85      /**
86       * Encipher the data in place, if there is a key to unlock it.
87       * 
88       * @param data
89       */
90      public void encipher(byte[] data) {
91          // Enciphering and deciphering are the same!
92          decipher(data);
93      }
94  
95      public URI getExpandedDataPath() throws BookException {
96          URI loc = NetUtil.lengthenURI(bmd.getLibrary(), (String) bmd.getProperty(ConfigEntryType.DATA_PATH));
97  
98          if (loc == null) {
99              // FIXME(DMS): missing parameter
100             throw new BookException(JSOtherMsg.lookupText("Missing data files for old and new testaments in {0}."));
101         }
102 
103         return loc;
104     }
105 
106     /**
107      * Initialize a AbstractBackend before use. This method needs to call
108      * addKey() a number of times on GenBookBackend
109      */
110     public Key readIndex() {
111         // TODO(dms): Eliminate readIndex by deriving GenBookBackend from
112         // AbstractKeyBackend
113         return null;
114     }
115 
116     /**
117      * Determine whether this Book contains the key in question
118      * 
119      * @param key
120      *            The key whose presence is desired.
121      * @return true if the Book contains the key
122      */
123     public abstract boolean contains(Key key);
124 
125     /**
126      * Get the text allotted for the given entry
127      * 
128      * @param key
129      *            The key to fetch
130      * @return String The data for the verse in question
131      * @throws BookException
132      *             If the data can not be read.
133      */
134     public abstract String getRawText(Key key) throws BookException;
135 
136     /**
137      * Set the text allotted for the given verse
138      * 
139      * @param key
140      *            The key to set text to
141      * @param text
142      *            The text to be set for key
143      * @throws BookException
144      *             If the data can not be set.
145      * @throws IOException
146      *             If the module data path could not be created.
147      */
148     public abstract void setRawText(Key key, String text) throws BookException, IOException;
149 
150     /**
151      * Sets alias for a comment on a verse range
152      * I.e. setRawText() was for verse range Gen.1.1-3 then setAliasKey should be called for Gen.1.1.2 and Gen.1.1.3
153      * @param alias Alias Key
154      * @param source Source Key
155      * @throws IOException Exception when anything goes wrong on writing the alias
156      */
157     public abstract void setAliasKey(Key alias, Key source) throws IOException;
158 
159     /**
160      * Create the directory to hold the Book if it does not exist.
161      * 
162      * @throws IOException
163      * @throws BookException
164      */
165     public void create() throws IOException, BookException {
166         File dataPath = new File(getExpandedDataPath());
167         if (!dataPath.exists() && !dataPath.mkdirs()) {
168             throw new IOException("Unable to create module data path!");
169         }
170     }
171 
172     /**
173      * Returns whether this AbstractBackend is implemented.
174      * 
175      * @return true if this AbstractBackend is implemented.
176      */
177     public boolean isSupported() {
178         return true;
179     }
180 
181     /**
182      * A Backend is writable if the file system allows the underlying files to
183      * be opened for writing and if the backend has implemented writing.
184      * Ultimately, all drivers should allow writing. At this time writing is not
185      * supported by backends, so abstract implementations should return false
186      * and let specific implementations return true otherwise.
187      * 
188      * @return true if the book is writable
189      */
190     public boolean isWritable() {
191         return false;
192     }
193 
194     private SwordBookMetaData bmd;
195 }
196