1 package net.sf.openrocket.database;
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;
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;
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.
33 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
35 public class Database<T extends Comparable<T>> extends AbstractSet<T> {
36 private static final LogHelper log = Application.getLogger();
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;
48 public Database(Loader<T> loader) {
55 public Iterator<T> iterator() {
56 return new DBIterator();
65 public boolean add(T element) {
68 index = Collections.binarySearch(list, element);
70 // List might contain the element
71 if (list.contains(element)) {
77 list.add(index, element);
78 fireAddEvent(element);
84 * Get the element with the specified index.
85 * @param index the index to retrieve.
86 * @return the element at the index.
88 public T get(int index) {
89 return list.get(index);
93 * Return the index of the given <code>Motor</code>, or -1 if not in the database.
96 * @return the index of the motor
98 public int indexOf(T m) {
99 return list.indexOf(m);
103 public void addDatabaseListener(DatabaseListener<T> listener) {
104 listeners.add(listener);
107 public void removeChangeListener(DatabaseListener<T> listener) {
108 listeners.remove(listener);
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);
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);
131 //////// Directory loading
133 public void load( String dir, final String pattern ) throws IOException {
135 FileFilter filter = new FileFilter() {
138 public boolean accept(File pathname) {
139 return pathname.getName().matches(pattern);
144 FileIterator files = DirectoryIterator.findDirectory(dir, filter);
145 while( files != null && files.hasNext() ) {
146 Pair<String, InputStream> file = files.next();
148 this.addAll(loader.load(file.getV(), file.getU()));
149 } catch (IOException e) {
150 log.warn("Error loading file " + file + ": " + e.getMessage(), e);
153 if ( files != null ) {
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>)
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).
168 public void loadDirectory(File dir, final String pattern) throws IOException {
169 if (loader == null) {
170 throw new IllegalStateException("no file loader set");
173 File[] files = dir.listFiles(new FilenameFilter() {
175 public boolean accept(File directory, String name) {
176 return name.matches(pattern);
180 throw new IOException("not a directory: " + dir);
182 for (File file : files) {
184 this.addAll(loader.load(new FileInputStream(file), file.getName()));
185 } catch (IOException e) {
186 log.warn("Error loading file " + file + ": " + e.getMessage(), e);
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.
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).
202 public void loadJarDirectory(String dir, String pattern) throws IOException {
204 // Process directory and extension
205 if (!dir.endsWith("/")) {
209 // Find and open the jar file this class is contained in
210 File file = JarUtil.getCurrentJarFile();
211 JarFile jarFile = new JarFile(file);
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)) {
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);
237 public void load(File file) throws IOException {
238 if (loader == null) {
239 throw new IllegalStateException("no file loader set");
241 this.addAll(loader.load(new FileInputStream(file), file.getName()));
247 * Iterator class implementation that fires changes if remove() is called.
249 private class DBIterator implements Iterator<T> {
250 private Iterator<T> iterator = list.iterator();
251 private T current = null;
254 public boolean hasNext() {
255 return iterator.hasNext();
260 current = iterator.next();
265 public void remove() {
267 fireRemoveEvent(current);