001    package edu.harvard.deas.hyperenc;
002    
003    import java.io.File;
004    import java.io.Serializable;
005    import java.util.ArrayList;
006    import java.util.Iterator;
007    import java.util.List;
008    import java.util.NoSuchElementException;
009    
010    import javax.mail.Address;
011    
012    /**
013     * A contact list backed by a database on disk. This class uses PersistentMap to
014     * store the Contacts to disk. Because PersistentMap provides an unsorted view
015     * of the data, this class also stores all Contacts in the list in a sorted
016     * collection in memory; all changes to this list are made in the in-memory copy
017     * and also written through to disk via the PersistentMap.
018     * <p>
019     * <code>DBContactList</code> attempts to read in any existing contact list from
020     * disk that has the same owner. If none is found, a new, empty DBContactList is
021     * created. Because entries are stored in the on-disk database in an inherently
022     * unsorted manner, the order of Contacts in a DBContactList may not be
023     * consistent across multiple sessions&mdash;the order is subject to change
024     * every time the data is read in from disk.
025     * 
026     * @see PersistentMap
027     */
028    public class DBContactList extends ContactList {
029      // ignore values and use a PersistentMap as a set -- kind of lame, but it works
030      private PersistentMap<Contact,Obj> myMap;
031      
032      // should always contain the same values as myMap.keySet()
033      private List<Contact> myList;
034      
035      // owner of this ContactList -- should be included in getContact calls, but not
036      // in get calls
037      private Contact myOwner;
038      
039      // placeholder class for values of the map
040      private static class Obj implements Serializable {
041        private static final long serialVersionUID = 1L;
042      }
043    
044      /**
045       * Creates a new DBContactList. Attempts to read in any existing contact list
046       * from disk that has the same owner. If none is found, a new, empty
047       * DBContactList is created. Because entries are stored in the on-disk
048       * database in an inherently unsorted manner, the order of Contacts in a
049       * DBContactList may not be consistent across multiple sessions&mdash;the
050       * order of the Contacts in the created list may be different from the order
051       * in which they were written by a previous instance of DBContactList.
052       * 
053       * @param owner
054       *        the owner of this list (usually the Contact corresponding to the
055       *        user of the hyper-encryption application)
056       * @param envPath
057       *        The path on disk of the database backing this map.
058       *        Must be readable and writable. If it doesn't
059       *        exist, it will be created. If <code>null</code>, the default
060       *        environment specified by {@link PersistentMap#DEFAULT_HOME} is used.
061       * @see PersistentMap
062       */
063      public DBContactList(Contact owner, File envPath) {
064        myMap = PersistentMap.getInstance(owner.getEmail().toString() + ".Contacts",
065                                          Contact.class,
066                                          Obj.class,
067                                          envPath);
068        myList = new ArrayList<Contact>(myMap.keySet());
069        myOwner = owner;
070      }
071    
072      /**
073       * Equivalent to <code>DBContactList(owner, null)</code>.
074       * 
075       * @param owner
076       *        the owner of this list (usually the Contact corresponding to the
077       *        user of the hyper-encryption application)
078       */
079      public DBContactList(Contact owner) {
080        this(owner, null);
081      }
082      
083      @Override
084      public Iterator<Contact> iterator() {
085        return myList.iterator();
086      }
087      
088      @Override
089      public void addContact(Contact c) {
090        myMap.put(c, new Obj());
091        myList.add(c);
092      }
093      
094      @Override
095      public void removeContact(Contact c) {
096        myMap.remove(c);
097        myList.remove(c);
098      }
099      
100      @Override
101      public Contact get(int index) {
102        return myList.get(index);
103      }
104      
105      @Override
106      public int size() {
107        return myList.size();
108      }
109      
110      @Override
111      public boolean isEmpty() {
112        return myList.isEmpty();
113      }
114      
115      /**
116       * Returns the Contact in this list with e-mail address <code>a</code>.
117       * @param a e-mail address
118       * @return the Contact with the given e-mail address
119       */
120      @Override
121      public Contact getContact(Address a) {
122        if (a.equals(myOwner.getEmail()))
123          return myOwner;
124        for (Contact c : myList)
125          if (a.equals(c.getEmail()))
126            return c;
127        throw new NoSuchElementException(
128            "Contact list does not contain a Contact with Address " + a);
129      }
130    }