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;
47 public Database(Loader<T> loader) {
53 public Iterator<T> iterator() {
54 return new DBIterator();
63 public boolean add(T element) {
66 index = Collections.binarySearch(list, element);
68 // List might contain the element
69 if (list.contains(element)) {
75 list.add(index,element);
82 * Get the element with the specified index.
83 * @param index the index to retrieve.
84 * @return the element at the index.
86 public T get(int index) {
87 return list.get(index);
91 * Return the index of the given <code>Motor</code>, or -1 if not in the database.
94 * @return the index of the motor
96 public int indexOf(T m) {
97 return list.indexOf(m);
102 public void addChangeListener(ChangeListener listener) {
103 listenerList .add(ChangeListener.class, listener);
108 public void removeChangeListener(ChangeListener listener) {
109 listenerList .remove(ChangeListener.class, listener);
113 protected void fireChangeEvent() {
114 Object[] listeners = listenerList.getListenerList();
115 ChangeEvent e = null;
116 for (int i = listeners.length-2; i>=0; i-=2) {
117 if (listeners[i]==ChangeListener.class) {
118 // Lazily create the event:
120 e = new ChangeEvent(this);
121 ((ChangeListener)listeners[i+1]).stateChanged(e);
128 //////// Directory loading
133 * Load all files in a directory to the motor database. Only files with file
134 * names matching the given pattern (as matched by <code>String.matches(String)</code>)
137 * @param dir the directory to read.
138 * @param pattern the pattern to match the file names to.
139 * @throws IOException if an IO error occurs when reading the JAR archive
140 * (errors reading individual files are printed to stderr).
142 public void loadDirectory(File dir, final String pattern) throws IOException {
143 if (loader == null) {
144 throw new IllegalStateException("no file loader set");
147 File[] files = dir.listFiles(new FilenameFilter() {
149 public boolean accept(File dir, String name) {
150 return name.matches(pattern);
154 throw new IOException("not a directory: "+dir);
156 for (File file: files) {
158 this.addAll(loader.load(new FileInputStream(file), file.getName()));
159 } catch (IOException e) {
160 System.err.println("Error loading file "+file+": " + e.getMessage());
167 * Read all files in a directory contained in the JAR file that this class belongs to.
168 * Only files whose names match the given pattern (as matched by
169 * <code>String.matches(String)</code>) will be read.
171 * @param dir the directory within the JAR archive to read.
172 * @param pattern the pattern to match the file names to.
173 * @throws IOException if an IO error occurs when reading the JAR archive
174 * (errors reading individual files are printed to stderr).
176 public void loadJarDirectory(String dir, String pattern) throws IOException {
178 // Process directory and extension
179 if (!dir.endsWith("/")) {
183 // Find and open the jar file this class is contained in
184 File file = JarUtil.getCurrentJarFile();
185 JarFile jarFile = new JarFile(file);
189 // Loop through JAR entries searching for files to load
190 Enumeration<JarEntry> entries = jarFile.entries();
191 while (entries.hasMoreElements()) {
192 JarEntry entry = entries.nextElement();
193 String name = entry.getName();
194 if (name.startsWith(dir) && name.matches(pattern)) {
196 InputStream stream = jarFile.getInputStream(entry);
197 this.addAll(loader.load(stream, name));
198 } catch (IOException e) {
199 System.err.println("Error loading file " + file + ": "
212 public void load(File file) throws IOException {
213 if (loader == null) {
214 throw new IllegalStateException("no file loader set");
216 this.addAll(loader.load(new FileInputStream(file), file.getName()));
222 * Iterator class implementation that fires changes if remove() is called.
224 private class DBIterator implements Iterator<T> {
225 private Iterator<T> iterator = list.iterator();
228 public boolean hasNext() {
229 return iterator.hasNext();
234 return iterator.next();
238 public void remove() {