View Javadoc
1   /*
2    * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.geometry.io;
17  
18  import java.io.Closeable;
19  import java.io.File;
20  import java.io.IOException;
21  import java.nio.channels.FileChannel;
22  
23  /**
24   * Abstract class defining the interface for file loaders.
25   * Specific implementations must be done for each file format to be supported.
26   */
27  public abstract class Loader implements Closeable {
28  
29      /**
30       * Default limit of bytes to keep mapped in memory.
31       */
32      public static final long DEFAULT_FILE_SIZE_LIMIT_TO_KEEP_IN_MEMORY = 50000000;
33  
34      /**
35       * Instance in charge of reading data from file.
36       */
37      protected AbstractFileReaderAndWriter reader;
38  
39      /**
40       * Boolean indicating that this instance is locked because decoding is being
41       * done.
42       */
43      protected boolean locked;
44  
45      /**
46       * Listener in charge of notifying when the decoding starts, ends or
47       * progress events.
48       */
49      protected LoaderListener listener;
50  
51      /**
52       * File to be read.
53       */
54      protected File file;
55  
56      /**
57       * Limit of bytes to keep mapped in memory. If provided file exceeds this
58       * value, then it is not mapped into memory.
59       */
60      private long fileSizeLimitToKeepInMemory;
61  
62      /**
63       * Default Constructor.
64       */
65      protected Loader() {
66          fileSizeLimitToKeepInMemory = DEFAULT_FILE_SIZE_LIMIT_TO_KEEP_IN_MEMORY;
67          reader = null;
68          locked = false;
69          listener = null;
70      }
71  
72      /**
73       * Constructor.
74       *
75       * @param f file to be read.
76       * @throws IOException raised if provided file does not exist or an I/O
77       *                     exception occurs.
78       */
79      protected Loader(final File f) throws IOException {
80          fileSizeLimitToKeepInMemory = DEFAULT_FILE_SIZE_LIMIT_TO_KEEP_IN_MEMORY;
81          file = f;
82          if (f.length() < fileSizeLimitToKeepInMemory) {
83              reader = new MappedFileReaderAndWriter(f, FileChannel.MapMode.READ_ONLY);
84          } else {
85              reader = new FileReaderAndWriter(f, FileChannel.MapMode.READ_ONLY);
86          }
87          locked = false;
88          listener = null;
89      }
90  
91      /**
92       * Constructor.
93       *
94       * @param listener listener to notify start, end and progress events.
95       */
96      protected Loader(final LoaderListener listener) {
97          fileSizeLimitToKeepInMemory = DEFAULT_FILE_SIZE_LIMIT_TO_KEEP_IN_MEMORY;
98          reader = null;
99          locked = false;
100         this.listener = listener;
101     }
102 
103     /**
104      * Constructor.
105      *
106      * @param f        file to be read.
107      * @param listener listener to notify start, end and progress events.
108      * @throws IOException raised if provided file does not exist or an I/O
109      *                     exception occurs.
110      */
111     protected Loader(final File f, final LoaderListener listener) throws IOException {
112         fileSizeLimitToKeepInMemory = DEFAULT_FILE_SIZE_LIMIT_TO_KEEP_IN_MEMORY;
113         file = f;
114         if (f.length() < fileSizeLimitToKeepInMemory) {
115             reader = new MappedFileReaderAndWriter(f, FileChannel.MapMode.READ_ONLY);
116         } else {
117             reader = new FileReaderAndWriter(f, FileChannel.MapMode.READ_ONLY);
118         }
119         locked = false;
120         this.listener = listener;
121     }
122 
123     /**
124      * Get maximum size (in bytes) to determine whether a file is completely
125      * cached in memory (if lower than maximum size), or if it is just streamed
126      * (if greater than maximum size).
127      *
128      * @return Maximum size to determine whether file is cached in memory or not.
129      */
130     public long getFileSizeLimitToKeepInMemory() {
131         return fileSizeLimitToKeepInMemory;
132     }
133 
134     /**
135      * Sets maximum size (in bytes) to determine whether a file is completely
136      * cached in memory (if lower than maximum size), or if it is just streamed
137      * (if greater than maximum size).
138      *
139      * @param fileSizeLimitToKeepInMemory maximum size to determine whether file
140      *                                    is cached in memory or not.
141      * @throws LockedException if loader is locked because it is currently
142      *                         processing a file.
143      */
144     public void setFileSizeLimitToKeepInMemory(final long fileSizeLimitToKeepInMemory) throws LockedException {
145         if (isLocked()) {
146             throw new LockedException();
147         }
148 
149         this.fileSizeLimitToKeepInMemory = fileSizeLimitToKeepInMemory;
150     }
151 
152     /**
153      * Indicates whether a file to be loaded has already been set.
154      *
155      * @return True if file has already been provided, false otherwise.
156      */
157     public boolean hasFile() {
158         return reader != null;
159     }
160 
161     /**
162      * Sets file to be loaded.
163      *
164      * @param f file to be loaded.
165      * @throws LockedException raised if this instance is loaded because a file
166      *                         is already being loaded.
167      * @throws IOException     raised if provided file does not exist or if an I/O
168      *                         exception occurs.
169      */
170     @SuppressWarnings("DuplicatedCode")
171     public void setFile(final File f) throws LockedException, IOException {
172         if (isLocked()) {
173             throw new LockedException();
174         }
175         file = f;
176 
177         if (reader != null) {
178             // close previous file
179             reader.close();
180         }
181 
182         if (f.length() < fileSizeLimitToKeepInMemory) {
183             reader = new MappedFileReaderAndWriter(f, FileChannel.MapMode.READ_ONLY);
184         } else {
185             reader = new FileReaderAndWriter(f, FileChannel.MapMode.READ_ONLY);
186         }
187     }
188 
189     /**
190      * Closes file provided to this loader.
191      *
192      * @throws IOException if an I/O error occurs.
193      */
194     @Override
195     public void close() throws IOException {
196         if (reader != null) {
197             reader.close();
198             reader = null;
199         }
200     }
201 
202     /**
203      * Determines whether this instance is locked.
204      * A loader remains locked while decoding of a file is being done.
205      * This instance will remain locked once the loading process starts until
206      * it finishes either successfully or not.
207      * While this instance remains locked no parameters can be changed,
208      * otherwise a LockedException will be raised.
209      *
210      * @return True if instance is locked, false otherwise.
211      */
212     public synchronized boolean isLocked() {
213         return locked;
214     }
215 
216     /**
217      * Sets the lock value of this instance so that no parameters cannot be
218      * changed until this instance is unlocked.
219      *
220      * @param locked value that determines whether this instance is locked or
221      *               not.
222      */
223     protected synchronized void setLocked(final boolean locked) {
224         this.locked = locked;
225     }
226 
227     /**
228      * Returns listener of this instance.
229      * A listener notifies of start, end and progress change events.
230      *
231      * @return listener of this instance.
232      */
233     public LoaderListener getListener() {
234         return listener;
235     }
236 
237     /**
238      * Sets listener of this instance.
239      * A listener notifies of start, end and progress change events.
240      *
241      * @param listener listener of this instance.
242      * @throws LockedException Raised if this instance is already locked.
243      */
244     public void setListener(final LoaderListener listener) throws LockedException {
245         if (isLocked()) {
246             throw new LockedException();
247         }
248         this.listener = listener;
249     }
250 
251     /**
252      * Determines whether enough parameters have been set so that the loading
253      * process can start.
254      *
255      * @return True if this instance is ready to start loading a file, false
256      * otherwise.
257      */
258     public abstract boolean isReady();
259 
260     /**
261      * Returns mesh format this instance is capable of loading.
262      *
263      * @return Mesh format.
264      */
265     public abstract MeshFormat getMeshFormat();
266 
267     /**
268      * Determines if provided file is a valid file that can be read by this
269      * loader.
270      *
271      * @return true if file is valid, false otherwise.
272      * @throws LockedException raised if this instance is already locked.
273      * @throws IOException     if an I/O error occurs.
274      */
275     public abstract boolean isValidFile() throws LockedException, IOException;
276 
277     /**
278      * Starts the loading process of provided file.
279      * This method returns a LoaderIterator to start the iterative process to
280      * load a file in small chunks of data.
281      *
282      * @return a loader iterator to read the file in a step-by-step process.
283      * @throws LockedException   raised if this instance is already locked.
284      * @throws NotReadyException raised if this instance is not yet ready.
285      * @throws IOException       if an I/O error occurs.
286      * @throws LoaderException   if file is corrupted or cannot be interpreted.
287      */
288     public abstract LoaderIterator load() throws LockedException, NotReadyException, IOException, LoaderException;
289 }