1 package net.sf.openrocket.database;
4 import java.io.FileInputStream;
5 import java.io.FilenameFilter;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.util.AbstractSet;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.Enumeration;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.jar.JarEntry;
15 import java.util.jar.JarFile;
17 import javax.swing.event.ChangeEvent;
18 import javax.swing.event.ChangeListener;
19 import javax.swing.event.EventListenerList;
21 import net.sf.openrocket.file.Loader;
22 import net.sf.openrocket.util.ChangeSource;
23 import net.sf.openrocket.util.JarUtil;
28 * A database set. This class functions as a <code>Set</code> that contains items
29 * of a specific type. Additionally, the items can be accessed via an index number.
30 * The elements are always kept in their natural order.
32 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
35 // TODO: HIGH: Database saving
36 public class Database<T extends Comparable<T>> extends AbstractSet<T> implements ChangeSource {
38 private final List<T> list = new ArrayList<T>();
39 private final EventListenerList listenerList = new EventListenerList();
40 private final Loader<T> loader;
41 private final DatabaseStore<T> store;
49 public Database(Loader<T> loader) {
54 public Database(DatabaseStore<T> store) {
59 public Database(Loader<T> loader, DatabaseStore<T> store) {
66 public Iterator<T> iterator() {
67 return new DBIterator();
76 public boolean add(T element) {
79 index = Collections.binarySearch(list, element);
81 // List might contain the element
82 if (list.contains(element)) {
88 list.add(index,element);
90 store.elementAdded(element);
97 * Get the element with the specified index.
98 * @param index the index to retrieve.
99 * @return the element at the index.
101 public T get(int index) {
102 return list.get(index);
106 * Return the index of the given <code>Motor</code>, or -1 if not in the database.
109 * @return the index of the motor
111 public int indexOf(T m) {
112 return list.indexOf(m);
117 public void addChangeListener(ChangeListener listener) {
118 listenerList .add(ChangeListener.class, listener);
123 public void removeChangeListener(ChangeListener listener) {
124 listenerList .remove(ChangeListener.class, listener);
128 protected void fireChangeEvent() {
129 Object[] listeners = listenerList.getListenerList();
130 ChangeEvent e = null;
131 for (int i = listeners.length-2; i>=0; i-=2) {
132 if (listeners[i]==ChangeListener.class) {
133 // Lazily create the event:
135 e = new ChangeEvent(this);
136 ((ChangeListener)listeners[i+1]).stateChanged(e);
143 //////// Directory loading
148 * Load all files in a directory to the motor database. Only files with file
149 * names matching the given pattern (as matched by <code>String.matches(String)</code>)
152 * @param dir the directory to read.
153 * @param pattern the pattern to match the file names to.
154 * @throws IOException if an IO error occurs when reading the JAR archive
155 * (errors reading individual files are printed to stderr).
157 public void loadDirectory(File dir, final String pattern) throws IOException {
158 if (loader == null) {
159 throw new IllegalStateException("no file loader set");
162 File[] files = dir.listFiles(new FilenameFilter() {
164 public boolean accept(File dir, String name) {
165 return name.matches(pattern);
169 throw new IOException("not a directory: "+dir);
171 for (File file: files) {
173 this.addAll(loader.load(new FileInputStream(file), file.getName()));
174 } catch (IOException e) {
175 System.err.println("Error loading file "+file+": " + e.getMessage());
182 * Read all files in a directory contained in the JAR file that this class belongs to.
183 * Only files whose names match the given pattern (as matched by
184 * <code>String.matches(String)</code>) will be read.
186 * @param dir the directory within the JAR archive to read.
187 * @param pattern the pattern to match the file names to.
188 * @throws IOException if an IO error occurs when reading the JAR archive
189 * (errors reading individual files are printed to stderr).
191 public void loadJarDirectory(String dir, String pattern) throws IOException {
193 // Process directory and extension
194 if (!dir.endsWith("/")) {
198 // Find and open the jar file this class is contained in
199 File file = JarUtil.getCurrentJarFile();
200 JarFile jarFile = new JarFile(file);
204 // Loop through JAR entries searching for files to load
205 Enumeration<JarEntry> entries = jarFile.entries();
206 while (entries.hasMoreElements()) {
207 JarEntry entry = entries.nextElement();
208 String name = entry.getName();
209 if (name.startsWith(dir) && name.matches(pattern)) {
211 InputStream stream = jarFile.getInputStream(entry);
212 this.addAll(loader.load(stream, name));
213 } catch (IOException e) {
214 System.err.println("Error loading file " + file + ": "
227 public void load(File file) throws IOException {
228 if (loader == null) {
229 throw new IllegalStateException("no file loader set");
231 this.addAll(loader.load(new FileInputStream(file), file.getName()));
237 * Iterator class implementation that fires changes if remove() is called.
239 private class DBIterator implements Iterator<T> {
240 private Iterator<T> iterator = list.iterator();
241 private T current = null;
244 public boolean hasNext() {
245 return iterator.hasNext();
250 current = iterator.next();
255 public void remove() {
258 store.elementRemoved(current);