On Tuesday 04 November 2008 12:13, xor at freenetproject.org wrote:
> Author: xor
> Date: 2008-11-04 12:13:55 +0000 (Tue, 04 Nov 2008)
> New Revision: 23306
> 
> Modified:
>    trunk/plugins/FMSPlugin/FMS.java
>    trunk/plugins/FMSPlugin/FMSBoard.java
>    trunk/plugins/FMSPlugin/FMSIdentityManager.java
>    trunk/plugins/FMSPlugin/FMSMessage.java
>    trunk/plugins/FMSPlugin/FMSMessageManager.java
>    trunk/plugins/FMSPlugin/WoT/FMSIdentityManagerWoT.java
>    trunk/plugins/FMSPlugin/WoT/FMSMessageManagerWoT.java
>    trunk/plugins/FMSPlugin/WoT/FMSMessageWoT.java
> Log:
> Major refactoring: FMS now completely relies on db4o, no more hashtables / 
linkedlists - if db4o works well then FMS should scale well. I changed so 
much of the code that I cannot split into small commits, sorry.
> 
> Modified: trunk/plugins/FMSPlugin/FMS.java
> ===================================================================
> --- trunk/plugins/FMSPlugin/FMS.java  2008-11-04 10:24:00 UTC (rev 23305)
> +++ trunk/plugins/FMSPlugin/FMS.java  2008-11-04 12:13:55 UTC (rev 23306)
> @@ -99,8 +99,11 @@
>               Configuration cache_config = Db4o.newConfiguration();
>               for(String f : FMSMessage.getIndexedFields())
>                       
> cache_config.objectClass(FMSMessage.class).objectField(f).indexed(true);
> +             
> cache_config.objectClass(FMSMessage.class).cascadeOnUpdate(true);
> +             // TODO: decide about cascade on delete. 
>               for(String f : FMSBoard.getIndexedFields())
>                       
> cache_config.objectClass(FMSBoard.class).objectField(f).indexed(true);
> +             
>               db_cache = Db4o.openFile(cache_config, "fms_cache.db4o");
>  
>               // while develop wipe cache on startup
> 
> Modified: trunk/plugins/FMSPlugin/FMSBoard.java
> ===================================================================
> --- trunk/plugins/FMSPlugin/FMSBoard.java     2008-11-04 10:24:00 UTC (rev 
23305)
> +++ trunk/plugins/FMSPlugin/FMSBoard.java     2008-11-04 12:13:55 UTC (rev 
23306)
> @@ -3,55 +3,33 @@
>   * http://www.gnu.org/ for further details of the GPL. */
>  package plugins.FMSPlugin;
>  
> -import java.util.Collections;
> -import java.util.HashSet;
> -import java.util.Hashtable;
>  import java.util.Iterator;
>  import java.util.LinkedList;
> -import java.util.ListIterator;
> -import java.util.NoSuchElementException;
>  
>  import com.db4o.ObjectContainer;
>  import com.db4o.ObjectSet;
>  import com.db4o.query.Query;
>  
>  import freenet.keys.FreenetURI;
> -import freenet.support.DoublyLinkedList;
>  import freenet.support.IndexableUpdatableSortedLinkedListItem;
>  import freenet.support.UpdatableSortedLinkedListItemImpl;
>  import freenet.support.UpdatableSortedLinkedListKilledException;
>  import freenet.support.UpdatableSortedLinkedListWithForeignIndex;
> -import freenet.support.DoublyLinkedList.Item;
>  
>  /**
>   * @author xor
>   *
>   */
> -public class FMSBoard extends UpdatableSortedLinkedListItemImpl implements 
IndexableUpdatableSortedLinkedListItem {
> -     
> -     private ObjectContainer db;
> +public class FMSBoard {
>  
> -     /**
> -      * Contains all threads in this board, both as a Hashmap and as a 
> linked 
list which is sorted by date.
> -      * The hashmap is useful for checking whether a message was already 
stored.
> -      * The linked list allows fast displaying of all messages.
> -      */
> -     private UpdatableSortedLinkedListWithForeignIndex mThreads = new 
UpdatableSortedLinkedListWithForeignIndex();
> -     
> -     /**
> -      * Contains orphans for which even the parent thread did not exist. 
> They 
are currently listed in the threads list and have to be removed
> -      * from it if their thread is found.
> -      */
> -     private LinkedList<FMSMessage> mAbsoluteOrphans = new 
LinkedList<FMSMessage>(); 
> +     private transient final ObjectContainer db;
>  
> -     private final FMSBoard self = this;
> -     
> -     private final FMSMessageManager mMessageManager;
> -     
> +     private transient final FMSBoard self = this;
> +
> +     private transient final FMSMessageManager mMessageManager;
> +
>       private final String mName;
> -     
> -     private String mDescription;
> -     
> +
>       /**
>        * Get a list of fields which the database should create an index on.
>        */
> @@ -59,65 +37,56 @@
>               return new String[] {"mName"};
>       }
>       
> -     public FMSBoard(FMSMessageManager newMessageManager, String newName, 
String newDescription) {
> +     public FMSBoard(ObjectContainer myDB, FMSMessageManager 
> newMessageManager, 
String newName) {
>               if(newName==null || newName.length() == 0)
>                       throw new IllegalArgumentException("Empty board name.");
> -             
> +
> +             assert(myDB != null);
>               assert(newMessageManager != null);
> +
> +             db = myDB;
>               mMessageManager = newMessageManager;
> -             // FIXME: Remove anything dangerous from name and description.
> +             // FIXME: Validate name and description.
>               mName = newName;
> -             setDescription(newDescription);
> +             
> +             db.store(this);
> +             db.commit();
>       }
>  
>       /**
> -      * @return the Description
> +      * @return The name.
>        */
> -     public String getDescription() {
> -             return mDescription;
> -     }
> -     
> -     /**
> -      * @param description The description to set.
> -      */
> -     public void setDescription(String newDescription) {
> -             //FIXME: Remove anything dangerous from description.
> -             mDescription = newDescription!=null ? newDescription : "";
> -     }
> -
> -     /**
> -      * @return The name
> -      */
>       public String getName() {
>               return mName;
>       }
> -     
> +
>       /**
>        * Called by the <code>FMSMessageManager</code> to add a just received 
message to the board.
>        * The job for this function is to find the right place in the 
> thread-tree 
for the new message and to move around older messages
>        * if a parent message of them is received.
>        */
> -     public void addMessage(FMSMessage newMessage) throws 
UpdatableSortedLinkedListKilledException {      
> -             if(newMessage.isThread()) {     /* The message is a thread */
> -                     mThreads.add(newMessage);
> -             }
> -             else
> +     public synchronized void addMessage(FMSMessage newMessage) throws 
UpdatableSortedLinkedListKilledException {      
> +             db.store(newMessage);
> +             db.commit();
> +
> +             if(!newMessage.isThread())
>               {
>                       FreenetURI parentURI = newMessage.getParentURI();
>                       FMSMessage parentMessage = 
> mMessageManager.get(parentURI); /* TODO: This 
allows crossposting. Figure out whether we need to handle it specially */
> +                     FMSMessage parentThread = findParentThread(newMessage);
> +
> +                     if(parentThread != null)
> +                             newMessage.setThread(parentThread);
> +
>                       if(parentMessage != null) {
> -                             parentMessage.addChild(newMessage);
> -                             db.store(parentMessage);
> +                             newMessage.setParent(parentMessage);

Are you forgetting to store newMessage?

>                       } else { /* The message is an orphan */
> -                             FMSMessage parentThread = 
mMessageManager.get(newMessage.getParentThreadURI());
>                               if(parentThread != null) {
> -                                     parentThread.addChild(newMessage);      
> /* We found its parent thread so 
just stick it in there for now */
> -                                     db.store(parentThread);
> +                                     newMessage.setParent(parentThread);     
> /* We found its parent thread so 
just stick it in there for now */
>                               }
> -                             else { /* The message is an absolute orphan */
> -                                     mThreads.add(newMessage); /* TODO: 
> Instead of hiding the message 
completely, we make it look like a thread. Reconsider this. */
> -                                     mAbsoluteOrphans.add(newMessage);
> -                                     
> +                             else {
> +                                      /* The message is an absolute orphan */
> +
>                                       /* 
>                                        * FIXME: The MessageManager should try 
> to download the parent message 
if it's poster has enough trust.
>                                        * If it is programmed to do that, it 
> will check its Hashtable whether 
the parent message already exists.
> @@ -126,70 +95,84 @@
>                               }
>                       } 
>               }
> -             
> -             db.commit();
> -             
> +
>               linkOrphansToNewParent(newMessage);
>       }
> -     
> -     private void linkOrphansToNewParent(FMSMessage newMessage) throws 
UpdatableSortedLinkedListKilledException {
> +
> +     private synchronized void linkOrphansToNewParent(FMSMessage newMessage) 
throws UpdatableSortedLinkedListKilledException {
>               if(newMessage.isThread()) {
> -                     for(FMSMessage o : mAbsoluteOrphans) {  /* Search in 
> the orphans for 
messages which belong to this thread */
> -                             
> if(o.getParentThreadURI().equals(newMessage.getURI())) {
> -                                     newMessage.addChild(o);
> -                                     mAbsoluteOrphans.remove(o);
> -                                     mThreads.remove(o);
> -                             }
> +                     Iterator<FMSMessage> absoluteOrphans = 
absoluteOrphanIterator(newMessage.getURI());
> +                     while(absoluteOrphans.hasNext()){       /* Search in 
> the absolute orphans for 
messages which belong to this thread  */
> +                             FMSMessage orphan = absoluteOrphans.next();
> +                             orphan.setParent(newMessage);
>                       }
>               }
>               else {
> -                     FMSMessage parentThread = 
(FMSMessage)mThreads.get(newMessage.getParentThreadURI());
> +                     FMSMessage parentThread = newMessage.getThread();
>                       if(parentThread != null) {      /* Search in its parent 
> thread for its 
children */
> -                             Iterator<FMSMessage> iter = 
> parentThread.childrenIterator();
> +                             Iterator<FMSMessage> iter = 
> parentThread.childrenIterator(this);
>                               while(iter.hasNext()) {
>                                       FMSMessage parentThreadChild = 
> iter.next();
>                                       
>                                       
> if(parentThreadChild.getParentURI().equals(newMessage.getURI())) { /* 
We found its parent, yeah! */
> -                                             iter.remove();  /* It's a child 
> of the newMessage, not of the 
parentThread */
> -                                             
> newMessage.addChild(parentThreadChild);
> +                                             
> parentThreadChild.setParent(newMessage); /* It's a child of the 
newMessage, not of the parentThread */
>                                       }
>                               }
> -                             db.store(parentThread);
> -                     } else { /* The new message is an absolute orphan, find 
> its children 
amongst the other absolute orphans */
> -                             for(FMSMessage o : mAbsoluteOrphans) {
> -                                     
> if(o.getParentURI().equals(newMessage.getURI())) {
> -                                             newMessage.addChild(o);
> -                                             mAbsoluteOrphans.remove(o);
> -                                     }
> +                     }
> +                     else { /* The new message is an absolute orphan, find 
> its children 
amongst the other absolute orphans */
> +                             Iterator<FMSMessage> absoluteOrphans = 
absoluteOrphanIterator(newMessage.getURI());
> +                             while(absoluteOrphans.hasNext()){       /* 
> Search in the orphans for messages 
which belong to this message  */
> +                                     FMSMessage orphan = 
> absoluteOrphans.next();
> +                                     /*
> +                                      * The following if() could be joined 
> into the db4o query in 
absoluteOrphanIterator(). I did not do it because we could
> +                                      * cache the list of absolute orphans 
> locally. 
> +                                      */
> +                                     
> if(orphan.getParentURI().equals(newMessage.getURI()))
> +                                             orphan.setParent(newMessage);
>                               }
>                       }
>               }
> +     }
> +     
> +     protected synchronized FMSMessage findParentThread(FMSMessage m) {
> +             Query q = db.query();
> +             q.constrain(FMSMessage.class);
> +             /* FIXME: I assume that db4o is configured to keep an URI index 
> per 
board. We still have to ensure in FMS.java that it is configured to do so.
> +              * If my second assumption - that the descend() statements are 
> evaluated 
in the specified order - is true, then it might be faste because the
> +              * URI index is smaller per board than the global URI index. */
> +             q.descend("mBoards").constrain(mName); 
> +             q.descend("mURI").constrain(m.getParentThreadURI());

You might need to do and() here, according to the api docs:
q.descend("mBoards").constain(mName).and(q.descend("mURI").constrain(m.getParentThreadURI());

> +             ObjectSet<FMSMessage> parents = q.execute();
>               
> -             db.store(newMessage);
> -             db.commit();
> +             assert(parents.size() <= 1);
> +             
> +             return (parents.size() != 0 ? parents.next()  : null);
>       }
>       
>  
>       /**
> -      * Get all messages in the board. The view is specified to the 
FMSOwnIdentity displaying it, therefore you have to pass one as parameter.
> +      * Get all threads in the board. The view is specified to the 
FMSOwnIdentity displaying it, therefore you have to pass one as parameter.
>        * @param identity The identity viewing the board.
>        * @return An iterator of the message which the identity will see 
> (based 
on its trust levels).
>        */
>       public synchronized Iterator<FMSMessage> threadIterator(final 
FMSOwnIdentity identity) {
>               return new Iterator<FMSMessage>() {
>                       private final FMSOwnIdentity mIdentity = identity;
> -                     private final ObjectSet<FMSMessage> mMessages;
>                       private final Iterator<FMSMessage> iter;
>                       private FMSMessage next;
>                        
>                       {
> +                             /* FIXME: If db4o supports precompiled queries, 
> this one should be 
stored precompiled.
> +                              * Reason: We sort the threads by date.
> +                              * Maybe we can just keep the Query-object and 
> call q.execute() as many 
times as we like to?
> +                              * Or somehow tell db4o to keep a per-board 
> thread index which is 
sorted by Date? - This would be the best solution */

Recommend you make a tag object which is only created for top level threads, 
then this becomes reasonably easy ...

>                               Query q = db.query();
>                               q.constrain(FMSMessage.class);
>                               q.descend("mBoards").constrain(mName); /* 
> FIXME: mBoards is an array. 
Does constrain() check whether it contains the element mName? */
> -                             q.descend("mParentURI").constrain(null);
> -                             q.orderDescending();
> -                             mMessages = q.execute();
> -                             iter = mMessages.iterator();
> +                             q.descend("mThread").constrain(null);
> +                             q.descend("mDate").orderDescending();
> +
> +                             iter = q.execute().iterator();
>                               next = iter.hasNext() ? iter.next() : null;
>                       }
>  
> @@ -214,19 +197,38 @@
>                       
>               };
>       }
> -
> -     /* (non-Javadoc)
> -      * @see 
freenet.support.IndexableUpdatableSortedLinkedListItem#indexValue()
> +     
> +     /**
> +      * Get an iterator over messages for which the parent thread with the 
given URI was not known. 
>        */
> -     public Object indexValue() {
> -             return mName;
> -     }
> +     public synchronized Iterator<FMSMessage> absoluteOrphanIterator(final 
FreenetURI thread) {
> +             return new Iterator<FMSMessage>() {
> +                     private final ObjectSet<FMSMessage> mMessages;
> +                     private final Iterator<FMSMessage> iter;
>  
> -     /* (non-Javadoc)
> -      * @see java.lang.Comparable#compareTo(java.lang.Object)
> -      */
> -     public int compareTo(Object o) {
> -             FMSBoard b = (FMSBoard)o;
> -             return mName.compareTo(b.getName());
> +                     {
> +                             /* FIXME: This query should be accelerated. The 
> amount of absolute 
orphans is very small usually, so we should configure db4o
> +                              * to keep a separate list of those. */

Again use a tag object.

> +                             Query q = db.query();
> +                             q.constrain(FMSMessage.class);
> +                             q.descend("mBoards").constrain(mName); /* 
> FIXME: mBoards is an array. 
Does constrain() check whether it contains the element mName? */
> +                             q.descend("mThreadURI").constrain(thread);
> +                             q.descend("mThread").constrain(null);
> +                             mMessages = q.execute();
> +                             iter = mMessages.iterator();
> +                     }
> +
> +                     public boolean hasNext() {
> +                             return mMessages.hasNext();
> +                     }
> +
> +                     public FMSMessage next() {
> +                             return mMessages.next();
> +                     }
> +
> +                     public void remove() {
> +                             throw new UnsupportedOperationException();
> +                     }
> +             };
>       }
>  }
> 
> Modified: trunk/plugins/FMSPlugin/FMSIdentityManager.java
> ===================================================================
> --- trunk/plugins/FMSPlugin/FMSIdentityManager.java   2008-11-04 10:24:00 UTC 
(rev 23305)
> +++ trunk/plugins/FMSPlugin/FMSIdentityManager.java   2008-11-04 12:13:55 UTC 
(rev 23306)
> @@ -3,21 +3,51 @@
>   * http://www.gnu.org/ for further details of the GPL. */
>  package plugins.FMSPlugin;
>  
> +import java.util.Iterator;
>  import java.util.Set;
>  
> +import com.db4o.ObjectContainer;
> +import com.db4o.ObjectSet;
> +
> +import plugins.FMSPlugin.WoT.FMSIdentityWoT;
> +import plugins.FMSPlugin.WoT.FMSOwnIdentityWoT;
> +
>  import freenet.support.Executor;
>  
>  /**
>   * @author saces, xor
> - *
> + * 
>   */
> -public abstract class FMSIdentityManager {
> +public abstract class FMSIdentityManager implements Iterable<FMSIdentity> {
>  
> -     private final Executor mExecutor;
> +     protected final ObjectContainer db;
>  
> -     public FMSIdentityManager(Executor newExecutor) {
> +     protected final Executor mExecutor;
> +
> +     public FMSIdentityManager(ObjectContainer myDB, Executor newExecutor) {
> +             db = myDB;
>               mExecutor = newExecutor;
> +     }
>  
> +     public synchronized Iterator<FMSIdentity> iterator() {
> +             ObjectSet<FMSIdentity> ids = db.query(FMSIdentity.class);
> +             return ids.iterator();
>       }
>  
> +     public synchronized Iterator<FMSOwnIdentity> ownIdentityIterator() {
> +             ObjectSet<FMSOwnIdentity> oids = db.query(FMSOwnIdentity.class);
> +             return oids.iterator();
> +     }
> +
> +     public synchronized boolean anyOwnIdentityWantsMessagesFrom(FMSIdentity 
identity) {
> +             Iterator<FMSOwnIdentity> iter = ownIdentityIterator();
> +
> +             while (iter.hasNext()) {
> +                     FMSOwnIdentity oid = iter.next();
> +                     if (oid.wantsMessagesFrom(identity))
> +                             return true;
> +             }
> +
> +             return false;
> +     }
>  }
> 
> Modified: trunk/plugins/FMSPlugin/FMSMessage.java
> ===================================================================
> --- trunk/plugins/FMSPlugin/FMSMessage.java   2008-11-04 10:24:00 UTC (rev 
23305)
> +++ trunk/plugins/FMSPlugin/FMSMessage.java   2008-11-04 12:13:55 UTC (rev 
23306)
> @@ -5,10 +5,14 @@
>  
>  import java.util.Arrays;
>  import java.util.Date;
> +import java.util.HashSet;
>  import java.util.Iterator;
>  import java.util.List;
>  import java.util.Set;
>  
> +import com.db4o.ObjectContainer;
> +import com.db4o.query.Query;
> +
>  import freenet.keys.FreenetURI;
>  import freenet.support.IndexableUpdatableSortedLinkedListItem;
>  import freenet.support.UpdatableSortedLinkedList;
> @@ -19,8 +23,10 @@
>   * @author saces, xor
>   *
>   */
> -public abstract class FMSMessage extends UpdatableSortedLinkedListItemImpl 
implements IndexableUpdatableSortedLinkedListItem {
> +public abstract class FMSMessage {
>       
> +     protected ObjectContainer db;
> +     

You cannot rely on db being kept up to date.

>       /**
>        * The URI of this message.
>        */
> @@ -63,18 +69,23 @@
>       private final FreenetURI[] mAttachments;
>       
>       /**
> -      * The replies to this messages.
> +      * The thread to which this message is a reply.
>        */
> -     private UpdatableSortedLinkedList mChildren = new 
UpdatableSortedLinkedList();
> +     private FMSMessage mThread = null;
>       
>       /**
> +      * The message to which this message is a reply.
> +      */
> +     private FMSMessage mParent = null;
> +     
> +     /**
>        * Get a list of fields which the database should create an index on.
>        */
>       public static String[] getIndexedFields() {
> -             return new String[] { "mURI", "mBoards" };
> +             return new String[] { "mURI", "mThreadURI", "mBoards"};
>       }
>       
> -     public FMSMessage(FreenetURI newURI, FreenetURI newThreadURI, 
> FreenetURI 
newParentURI, Set<FMSBoard> newBoards, FMSIdentity newAuthor, String 
newTitle, Date newDate, String newText, List<FreenetURI> newAttachments) {
> +     public FMSMessage(ObjectContainer db, FreenetURI newURI, FreenetURI 
newThreadURI, FreenetURI newParentURI, Set<FMSBoard> newBoards, FMSIdentity 
newAuthor, String newTitle, Date newDate, String newText, List<FreenetURI> 
newAttachments) {
>               if (newURI == null || newBoards == null || newAuthor == null)
>                       throw new IllegalArgumentException();
>               
> @@ -167,40 +178,64 @@
>               return mAttachments;
>       }
>       
> -     public synchronized void addChild(FMSMessage newChild) throws 
UpdatableSortedLinkedListKilledException {
> -             if(mChildren.contains(newChild)) {
> -                     assert(false); // TODO: check whether this should be 
> allowed to happen.
> -                     return;
> -             }
> -             
> -             mChildren.add(newChild);
> +     public synchronized FMSMessage getThread() {
> +             return mThread;
>       }
>       
> -     public synchronized Iterator<FMSMessage> childrenIterator() {
> -             return mChildren.iterator();
> +     public synchronized void setThread(FMSMessage newParentThread) {
> +             assert(mThread == null);
> +             assert(mThreadURI == null);
> +             mThread = newParentThread;
> +             db.store(this);
> +             db.commit();
>       }
>  
> -     /**
> -      * Compare by Date to the other FMSMessage.
> -      * @param o An object of type FMSMessage 
> -      */
> -     public int compareTo(Object o) {
> -             FMSMessage m = (FMSMessage)o;
> -             return mDate.compareTo(m.getDate());
> +     public synchronized FMSMessage getParent() {
> +             return mParent;
>       }
> +
> +     public synchronized void setParent(FMSMessage newParent) throws 
UpdatableSortedLinkedListKilledException {
> +             /* TODO: assert(newParent contains at least one board which 
> mBoards 
contains) */

Does that have to be true? In NNTP, the user can reply to any board he wants 
to reply to ...

> +             mParent = newParent;
> +             db.store(this);
> +             db.commit();
> +     }
>       
> -     /**
> -      * Get the index value for IndexableUpdatableSortedLinkedListItem.
> -      */
> -     public Object indexValue() {
> -             return mURI;
> +     public synchronized Iterator<FMSMessage> childrenIterator(final 
> FMSBoard 
board) {
> +             return new Iterator<FMSMessage>() {
> +                     private Iterator<FMSMessage> iter;
> +                     
> +                     {
> +                             /* TODO: Accelerate this query: configure db4o 
> to keep a per-message 
date-sorted index of children.
> +                              * - Not very important for now since threads 
> are usually small. */

I don't think that's possible. But as you said, it should be small, so 
grabbing all the children from an index (as below) and then sorting by date 
is not a big deal.

> +                             Query q = db.query();
> +                             q.constrain(FMSMessage.class);
> +                             q.descend("mBoard").constrain(board.getName());
> +                             q.descend("mParent").constrain(this);
> +                             q.descend("mDate").orderDescending();
> +                             
> +                             iter = q.execute().iterator();
> +                     }
> +
> +                     public boolean hasNext() {
> +                             return iter.hasNext();
> +                     }
> +
> +                     public FMSMessage next() {
> +                             return iter.next();
> +                     }
> +
> +                     public void remove() {
> +                             throw new UnsupportedOperationException("Use 
> child.setParent(null) 
instead.");
> +                     }
> +             };
>       }
>       
>       /**
>        * Checks whether the title of the message is valid. Validity 
> conditions:
>        * - ...
>        */
> -     static boolean isTitleValid(String title) {
> +     static public boolean isTitleValid(String title) {
>               // FIXME: Implement.
>               return true;
>       }
> @@ -209,7 +244,7 @@
>        * Checks whether the text of the message is valid. Validity conditions:
>        * - ...
>        */
> -     static boolean isTextValid(String text) {
> +     static public boolean isTextValid(String text) {
>               // FIXME: Implement.
>               return true;
>       }
> @@ -218,7 +253,7 @@
>        * Makes the passed title valid in means of <code>isTitleValid()</code>
>        * @see isTitleValid
>        */
> -     static String makeTitleValid(String title) {
> +     static public String makeTitleValid(String title) {
>               // FIXME: Implement.
>               return title;
>       }
> @@ -227,7 +262,7 @@
>        * Makes the passed text valid in means of <code>isTextValid()</code>
>        * @see isTextValid
>        */
> -     static String makeTextValid(String text) {
> +     static public String makeTextValid(String text) {
>               // FIXME: Implement.
>               return text;
>       }
> 
> Modified: trunk/plugins/FMSPlugin/FMSMessageManager.java
> ===================================================================
> --- trunk/plugins/FMSPlugin/FMSMessageManager.java    2008-11-04 10:24:00 UTC 
(rev 23305)
> +++ trunk/plugins/FMSPlugin/FMSMessageManager.java    2008-11-04 12:13:55 UTC 
(rev 23306)
> @@ -17,43 +17,59 @@
>   *
>   */
>  public abstract class FMSMessageManager {
> -     
> +
>       protected ObjectContainer db;
> -     
> -     /**
> -      * Contains all boards which where found in a message. References to 
> all 
messages of a board are stored in
> -      * the board. Adding a newly downloaded message therefore is done by 
searching its board and calling 
> -      * <code>addMessage()</code> on that board. Further, the message is 
> also 
added to mMessages, see below.
> -      */
> -     protected UpdatableSortedLinkedListWithForeignIndex mBoards = new 
UpdatableSortedLinkedListWithForeignIndex();
> -     
> +
> +     protected FMSIdentityManager mIdentityManager;
> +
> +     public FMSMessageManager(ObjectContainer myDB, FMSIdentityManager 
myIdentityManager) {
> +             assert(myDB != null);
> +             assert(myIdentityManager != null);
> +
> +             db = myDB;
> +             mIdentityManager = myIdentityManager;
> +     }
> +
>       public synchronized FMSMessage get(FreenetURI uri) {
>               Query query = db.query();
>               query.constrain(FMSMessage.class);
>               query.descend("mURI").constrain(uri);
>               ObjectSet<FMSMessage> result = query.execute();
> -             
> +
> +             assert(result.size() <= 1);
> +
>               return (result.size() == 0) ? null : result.next();
>       }
> -     
> +
>       public synchronized FMSBoard getBoardByName(String name) {
> -             return (FMSBoard)mBoards.get(name);
> +             Query query = db.query();
> +             query.constrain(FMSBoard.class);
> +             query.descend("mName").constrain(name);
> +             ObjectSet<FMSBoard> result = query.execute();
> +
> +             assert(result.size() <= 1);
> +
> +             return (result.size() == 0) ? null : result.next();
>       }
> -     
> +
>       /**
> -      * Get an iterator of boards which the identity could subscribe to.
> -      * @param identity
> -      * @return
> +      * Get an iterator of all boards.
>        */
> -     public synchronized Iterator<FMSBoard> boardIterator(FMSOwnIdentity 
identity) {
> -             return (Iterator<FMSBoard>)mBoards.iterator();
> +     public synchronized Iterator<FMSBoard> boardIterator() {
> +             /* FIXME: Accelerate this query. db4o should be configured to 
> keep an 
alphabetic index of boards */

No problem, just create the index...

> +             Query query = db.query();
> +             query.constrain(FMSBoard.class);
> +             query.descend("mName").orderDescending();

Why backwards?
> +
> +             ObjectSet<FMSBoard> result = query.execute();
> +
> +             return result.iterator();
>       }
> -     
>  
>       /**
>        * Returns true if the message was not downloaded yet and any of the 
FMSOwnIdentity wants the message.
> -      * @param uri
> -      * @return
>        */
> -     protected abstract boolean shouldDownloadMessage(FreenetURI uri);
> +     protected synchronized boolean shouldDownloadMessage(FreenetURI uri, 
FMSIdentity author) {
> +             return (get(uri) != null) || 
mIdentityManager.anyOwnIdentityWantsMessagesFrom(author);

Why do we return true here if we already have the message?

> +     }
>  }
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 827 bytes
Desc: not available
URL: 
<https://emu.freenetproject.org/pipermail/devl/attachments/20081111/799454fd/attachment.pgp>

Reply via email to