create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / database / Database.java
1 package net.sf.openrocket.database;
2
3 import java.io.File;
4 import java.io.FileFilter;
5 import java.io.FileInputStream;
6 import java.io.FilenameFilter;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.AbstractSet;
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.Enumeration;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.jar.JarEntry;
16 import java.util.jar.JarFile;
17
18 import net.sf.openrocket.file.Loader;
19 import net.sf.openrocket.file.iterator.DirectoryIterator;
20 import net.sf.openrocket.file.iterator.FileIterator;
21 import net.sf.openrocket.logging.LogHelper;
22 import net.sf.openrocket.startup.Application;
23 import net.sf.openrocket.util.JarUtil;
24 import net.sf.openrocket.util.Pair;
25
26
27
28 /**
29  * A database set.  This class functions as a <code>Set</code> that contains items
30  * of a specific type.  Additionally, the items can be accessed via an index number.
31  * The elements are always kept in their natural order.
32  * 
33  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
34  */
35 public class Database<T extends Comparable<T>> extends AbstractSet<T> {
36         private static final LogHelper log = Application.getLogger();
37         
38         protected final List<T> list = new ArrayList<T>();
39         private final ArrayList<DatabaseListener<T>> listeners =
40                         new ArrayList<DatabaseListener<T>>();
41         private final Loader<T> loader;
42         
43         
44         public Database() {
45                 loader = null;
46         }
47         
48         public Database(Loader<T> loader) {
49                 this.loader = loader;
50         }
51         
52         
53
54         @Override
55         public Iterator<T> iterator() {
56                 return new DBIterator();
57         }
58         
59         @Override
60         public int size() {
61                 return list.size();
62         }
63         
64         @Override
65         public boolean add(T element) {
66                 int index;
67                 
68                 index = Collections.binarySearch(list, element);
69                 if (index >= 0) {
70                         // List might contain the element
71                         if (list.contains(element)) {
72                                 return false;
73                         }
74                 } else {
75                         index = -(index + 1);
76                 }
77                 list.add(index, element);
78                 fireAddEvent(element);
79                 return true;
80         }
81         
82         
83         /**
84          * Get the element with the specified index.
85          * @param index the index to retrieve.
86          * @return              the element at the index.
87          */
88         public T get(int index) {
89                 return list.get(index);
90         }
91         
92         /**
93          * Return the index of the given <code>Motor</code>, or -1 if not in the database.
94          * 
95          * @param m   the motor
96          * @return        the index of the motor
97          */
98         public int indexOf(T m) {
99                 return list.indexOf(m);
100         }
101         
102         
103         public void addDatabaseListener(DatabaseListener<T> listener) {
104                 listeners.add(listener);
105         }
106         
107         public void removeChangeListener(DatabaseListener<T> listener) {
108                 listeners.remove(listener);
109         }
110         
111         
112
113         @SuppressWarnings("unchecked")
114         protected void fireAddEvent(T element) {
115                 Object[] array = listeners.toArray();
116                 for (Object l : array) {
117                         ((DatabaseListener<T>) l).elementAdded(element, this);
118                 }
119         }
120         
121         @SuppressWarnings("unchecked")
122         protected void fireRemoveEvent(T element) {
123                 Object[] array = listeners.toArray();
124                 for (Object l : array) {
125                         ((DatabaseListener<T>) l).elementRemoved(element, this);
126                 }
127         }
128         
129         
130
131         ////////  Directory loading
132         
133         public void load( String dir, final String pattern ) throws IOException {
134                 
135                 FileFilter filter = new FileFilter() {
136
137                         @Override
138                         public boolean accept(File pathname) {
139                                 return pathname.getName().matches(pattern);
140                         }
141                         
142                 };
143                 
144                 FileIterator files = DirectoryIterator.findDirectory(dir, filter);
145                 while( files != null && files.hasNext() ) {
146                         Pair<String, InputStream> file = files.next();
147                         try {
148                                 this.addAll(loader.load(file.getV(), file.getU()));
149                         } catch (IOException e) {
150                                 log.warn("Error loading file " + file + ": " + e.getMessage(), e);
151                         }
152                 }
153                 if ( files != null ) {
154                         files.close();
155                 }
156         }
157
158         /**
159          * Load all files in a directory to the motor database.  Only files with file
160          * names matching the given pattern (as matched by <code>String.matches(String)</code>)
161          * are processed.
162          * 
163          * @param dir                   the directory to read.
164          * @param pattern               the pattern to match the file names to.
165          * @throws IOException  if an IO error occurs when reading the JAR archive
166          *                                              (errors reading individual files are printed to stderr).
167          */
168         public void loadDirectory(File dir, final String pattern) throws IOException {
169                 if (loader == null) {
170                         throw new IllegalStateException("no file loader set");
171                 }
172                 
173                 File[] files = dir.listFiles(new FilenameFilter() {
174                         @Override
175                         public boolean accept(File directory, String name) {
176                                 return name.matches(pattern);
177                         }
178                 });
179                 if (files == null) {
180                         throw new IOException("not a directory: " + dir);
181                 }
182                 for (File file : files) {
183                         try {
184                                 this.addAll(loader.load(new FileInputStream(file), file.getName()));
185                         } catch (IOException e) {
186                                 log.warn("Error loading file " + file + ": " + e.getMessage(), e);
187                         }
188                 }
189         }
190         
191         
192         /**
193          * Read all files in a directory contained in the JAR file that this class belongs to.
194          * Only files whose names match the given pattern (as matched by
195          * <code>String.matches(String)</code>) will be read.
196          * 
197          * @param dir                   the directory within the JAR archive to read.
198          * @param pattern               the pattern to match the file names to.
199          * @throws IOException  if an IO error occurs when reading the JAR archive
200          *                                              (errors reading individual files are printed to stderr).
201          */
202         public void loadJarDirectory(String dir, String pattern) throws IOException {
203                 
204                 // Process directory and extension
205                 if (!dir.endsWith("/")) {
206                         dir += "/";
207                 }
208                 
209                 // Find and open the jar file this class is contained in
210                 File file = JarUtil.getCurrentJarFile();
211                 JarFile jarFile = new JarFile(file);
212                 
213                 try {
214                         
215                         // Loop through JAR entries searching for files to load
216                         Enumeration<JarEntry> entries = jarFile.entries();
217                         while (entries.hasMoreElements()) {
218                                 JarEntry entry = entries.nextElement();
219                                 String name = entry.getName();
220                                 if (name.startsWith(dir) && name.matches(pattern)) {
221                                         try {
222                                                 InputStream stream = jarFile.getInputStream(entry);
223                                                 this.addAll(loader.load(stream, name));
224                                         } catch (IOException e) {
225                                                 log.warn("Error loading file " + file + ": " + e.getMessage(), e);
226                                         }
227                                 }
228                         }
229                         
230                 } finally {
231                         jarFile.close();
232                 }
233         }
234         
235         
236
237         public void load(File file) throws IOException {
238                 if (loader == null) {
239                         throw new IllegalStateException("no file loader set");
240                 }
241                 this.addAll(loader.load(new FileInputStream(file), file.getName()));
242         }
243         
244         
245
246         /**
247          * Iterator class implementation that fires changes if remove() is called.
248          */
249         private class DBIterator implements Iterator<T> {
250                 private Iterator<T> iterator = list.iterator();
251                 private T current = null;
252                 
253                 @Override
254                 public boolean hasNext() {
255                         return iterator.hasNext();
256                 }
257                 
258                 @Override
259                 public T next() {
260                         current = iterator.next();
261                         return current;
262                 }
263                 
264                 @Override
265                 public void remove() {
266                         iterator.remove();
267                         fireRemoveEvent(current);
268                 }
269         }
270 }