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, 2013 - 2016
18   *
19   */
20  package org.crosswire.jsword.book.sword.state;
21  
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.RandomAccessFile;
25  import java.net.URI;
26  
27  import org.crosswire.common.util.FileUtil;
28  import org.crosswire.common.util.IOUtil;
29  import org.crosswire.common.util.NetUtil;
30  import org.crosswire.common.util.Reporter;
31  import org.crosswire.jsword.JSOtherMsg;
32  import org.crosswire.jsword.book.BookException;
33  import org.crosswire.jsword.book.BookMetaData;
34  import org.crosswire.jsword.book.sword.SwordConstants;
35  import org.crosswire.jsword.book.sword.SwordUtil;
36  import org.crosswire.jsword.versification.Testament;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * Stores the random access files required for processing the passage request
42   * 
43   * The caller is required to close to correctly free resources and avoid File
44   * pointer leaks
45   * 
46   * @see gnu.lgpl.License The GNU Lesser General Public License for details.
47   * @author DM Smith
48   */
49  public class RawBackendState extends AbstractOpenFileState {
50      /**
51       * This is default package access for forcing the use of the
52       * OpenFileStateManager to manage the creation. Not doing so may result in
53       * new instances of OpenFileState being created for no reason, and as a
54       * result, if they are released to the OpenFileStateManager by mistake this
55       * would result in leakage.
56       * 
57       * @param bookMetaData
58       *            the appropriate metadata for the book
59       */
60      RawBackendState(BookMetaData bookMetaData) throws BookException {
61          super(bookMetaData);
62          URI path = SwordUtil.getExpandedDataPath(bookMetaData);
63  
64          URI otPath = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_OT);
65          otTextFile = new File(otPath.getPath());
66          otIdxFile = new File(otPath.getPath() + SwordConstants.EXTENSION_VSS);
67  
68          URI ntPath = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_NT);
69          ntTextFile = new File(ntPath.getPath());
70          ntIdxFile = new File(ntPath.getPath() + SwordConstants.EXTENSION_VSS);
71  
72          // It is an error to be neither OT nor NT
73          // Throwing exception, as if we can't read either the ot or nt file,
74          // then we might as well give up
75          if (!otTextFile.canRead() && !ntTextFile.canRead()) {
76              BookException prob = new BookException(JSOtherMsg.lookupText("Missing data files for old and new testaments in {0}.", path));
77              Reporter.informUser(this, prob);
78              throw prob;
79          }
80  
81          String fileMode = isWritable() ? FileUtil.MODE_WRITE : FileUtil.MODE_READ;
82  
83          if (otIdxFile.canRead()) {
84              try {
85                  otIdxRaf = new RandomAccessFile(otIdxFile, fileMode);
86                  otTextRaf = new RandomAccessFile(otTextFile, fileMode);
87              } catch (FileNotFoundException ex) {
88                  //failed to open the files, so close them now
89                  IOUtil.close(otIdxRaf);
90                  IOUtil.close(otTextRaf);
91  
92                  assert false : ex;
93  
94                  LOGGER.error("Could not open OT", ex);
95                  ntIdxRaf = null;
96                  ntTextRaf = null;
97              }
98          }
99  
100         if (ntIdxFile.canRead()) {
101             try {
102                 ntIdxRaf = new RandomAccessFile(ntIdxFile, fileMode);
103                 ntTextRaf = new RandomAccessFile(ntTextFile, fileMode);
104             } catch (FileNotFoundException ex) {
105                 //failed to open the files, so close them now
106                 IOUtil.close(ntIdxRaf);
107                 IOUtil.close(ntTextRaf);
108 
109                 assert false : ex;
110                 LOGGER.error("Could not open NT", ex);
111                 ntIdxRaf = null;
112                 ntTextRaf = null;
113             }
114         }
115     }
116 
117     /* (non-Javadoc)
118      * @see org.crosswire.jsword.book.sword.AbstractBackend#isWritable()
119      */
120     public boolean isWritable() {
121         // For the module to be writable either the old testament or the new
122         // testament needs to be present
123         // (i.e. readable) and both the index and the data files need to be
124         // writable
125         if (otIdxFile.canRead() && (otIdxFile.canWrite() || !otTextFile.canWrite())) {
126             return false;
127         }
128         if (ntIdxFile.canRead() && (ntIdxFile.canWrite() || !ntTextFile.canWrite())) {
129             return false;
130         }
131         return otIdxFile.canRead() || ntIdxFile.canRead();
132     }
133 
134     public void releaseResources() {
135         IOUtil.close(ntIdxRaf);
136         IOUtil.close(ntTextRaf);
137         IOUtil.close(otIdxRaf);
138         IOUtil.close(otTextRaf);
139         ntIdxRaf = null;
140         ntTextRaf = null;
141         otIdxRaf = null;
142         otTextRaf = null;
143     }
144 
145     /**
146      * Get the index file for the given testament.
147      * 
148      * @param testament the testament for the file
149      * @return the requested file for the testament
150      */
151     public RandomAccessFile getIdxRaf(Testament testament) {
152         return testament == Testament.NEW ? ntIdxRaf : otIdxRaf;
153     }
154 
155     /**
156      * Get the text file for the given testament.
157      * 
158      * @param testament the testament for the file
159      * @return the requested file for the testament
160      */
161     public RandomAccessFile getTextRaf(Testament testament) {
162         return testament == Testament.NEW ? ntTextRaf : otTextRaf;
163     }
164 
165     /**
166      * @return the otTextRaf
167      */
168     public RandomAccessFile getOtTextRaf() {
169         return otTextRaf;
170     }
171 
172     /**
173      * @return the ntTextRaf
174      */
175     public RandomAccessFile getNtTextRaf() {
176         return ntTextRaf;
177     }
178 
179     /**
180      * Get the text file for the given testament.
181      * 
182      * @param testament the testament for the file
183      * @return the requested file for the testament
184      */
185     public File getTextFile(Testament testament) {
186         return testament == Testament.NEW ? ntTextFile : otTextFile;
187     }
188 
189     /**
190      * Get the index file for the given testament.
191      * 
192      * @param testament the testament for the file
193      * @return the requested file for the testament
194      */
195     public File getIdxFile(Testament testament) {
196         return testament == Testament.NEW ? ntIdxFile : otIdxFile;
197     }
198 
199     protected RandomAccessFile otIdxRaf;
200     protected RandomAccessFile ntIdxRaf;
201     protected RandomAccessFile otTextRaf;
202     protected RandomAccessFile ntTextRaf;
203     protected File ntIdxFile;
204     protected File ntTextFile;
205     protected File otIdxFile;
206     protected File otTextFile;
207 
208     /**
209      * The log stream
210      */
211     private static final Logger LOGGER = LoggerFactory.getLogger(RawBackendState.class);
212 }
213