Revision: 17173
          http://sourceforge.net/p/gate/code/17173
Author:   markagreenwood
Date:     2013-12-15 18:54:25 +0000 (Sun, 15 Dec 2013)
Log Message:
-----------
relations are now first class citizens; although possibly not the most well 
behaved

Modified Paths:
--------------
    gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/CorefBase.java
    
gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/LegacyCorefDataWriter.java
    gate/trunk/src/main/gate/SimpleAnnotationSet.java
    gate/trunk/src/main/gate/annotation/AnnotationSetImpl.java
    gate/trunk/src/main/gate/corpora/DocumentStaxUtils.java
    gate/trunk/src/main/gate/creole/annotdelete/AnnotationDeletePR.java
    gate/trunk/src/main/gate/relations/Relation.java
    gate/trunk/src/main/gate/relations/RelationSet.java
    gate/trunk/src/main/gate/relations/SimpleRelation.java
    gate/trunk/src/main/gate/resources/creole/creole.xml

Added Paths:
-----------
    gate/trunk/src/main/gate/gui/docview/RelationSetView.java

Modified: gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/CorefBase.java
===================================================================
--- gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/CorefBase.java 
2013-12-14 09:08:07 UTC (rev 17172)
+++ gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/CorefBase.java 
2013-12-15 18:54:25 UTC (rev 17173)
@@ -206,9 +206,9 @@
    * @return the {@link RelationSet} used during the {@link #execute()} call.
    */
   public RelationSet getRelationSet() {
-    return RelationSet.getRelations(
-        (annotationSetName == null || annotationSetName.trim().length() == 0) ?
-        document.getAnnotations() : 
document.getAnnotations(annotationSetName));
+    return (annotationSetName == null || annotationSetName.trim().length() == 
0)
+      ? document.getAnnotations().getRelations()
+      : document.getAnnotations(annotationSetName).getRelations();
   }
   
   @Override

Modified: 
gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/LegacyCorefDataWriter.java
===================================================================
--- 
gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/LegacyCorefDataWriter.java 
    2013-12-14 09:08:07 UTC (rev 17172)
+++ 
gate/trunk/plugins/Coref_Tools/src/gate/creole/coref/LegacyCorefDataWriter.java 
    2013-12-15 18:54:25 UTC (rev 17173)
@@ -85,7 +85,7 @@
         (annotationSetName == null || annotationSetName.trim().length() == 0) ?
         document.getAnnotations() : 
document.getAnnotations(annotationSetName); 
     // get the relations set
-    RelationSet relSet = RelationSet.getRelations(inputAnnSet);
+    RelationSet relSet = inputAnnSet.getRelations();
     
     // now create the annotation and document features from the relations
     Map<String, List<List<Integer>>> docCorefMap;

Modified: gate/trunk/src/main/gate/SimpleAnnotationSet.java
===================================================================
--- gate/trunk/src/main/gate/SimpleAnnotationSet.java   2013-12-14 09:08:07 UTC 
(rev 17172)
+++ gate/trunk/src/main/gate/SimpleAnnotationSet.java   2013-12-15 18:54:25 UTC 
(rev 17173)
@@ -16,12 +16,13 @@
 
 package gate;
 
+import gate.relations.RelationSet;
+import gate.util.InvalidOffsetException;
+
 import java.io.Serializable;
 import java.util.Iterator;
 import java.util.Set;
 
-import gate.util.InvalidOffsetException;
-
 /**
  * <p>
  * A set of annotations on a document. Simple annotation sets support
@@ -170,5 +171,7 @@
    * be attached to a document.
    */
   public Document getDocument();
+  
+  public RelationSet getRelations();
 
 } // interface SimpleAnnotationSet

Modified: gate/trunk/src/main/gate/annotation/AnnotationSetImpl.java
===================================================================
--- gate/trunk/src/main/gate/annotation/AnnotationSetImpl.java  2013-12-14 
09:08:07 UTC (rev 17172)
+++ gate/trunk/src/main/gate/annotation/AnnotationSetImpl.java  2013-12-15 
18:54:25 UTC (rev 17173)
@@ -31,17 +31,41 @@
  */
 package gate.annotation;
 
-import java.io.*;
-import java.util.*;
+import gate.Annotation;
+import gate.AnnotationSet;
+import gate.Document;
+import gate.DocumentContent;
+import gate.FeatureMap;
+import gate.Gate;
+import gate.GateConstants;
+import gate.Node;
+import gate.corpora.DocumentImpl;
+import gate.event.AnnotationSetEvent;
+import gate.event.AnnotationSetListener;
+import gate.event.GateEvent;
+import gate.event.GateListener;
+import gate.relations.RelationSet;
+import gate.util.InvalidOffsetException;
+import gate.util.RBTreeMap;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 
-import gate.*;
-import gate.corpora.DocumentImpl;
-import gate.event.*;
-import gate.util.*;
-
 /**
  * Implementation of AnnotationSet. Has a number of indices, all bar one of
  * which are null by default and are only constructed when asked for. Has lots
@@ -103,6 +127,8 @@
    * needs to.
    */
   protected transient Long longestAnnot = 0l;
+  
+  protected RelationSet relations = null;
 
   // Empty AnnotationSet to be returned instead of null
    public final static AnnotationSet emptyAnnotationSet;
@@ -1262,4 +1288,11 @@
     }
     annotations = null;
   }
+  
+  public RelationSet getRelations() {
+    if (relations == null) {
+      relations = new RelationSet(this);
+    }
+    return relations;
+  }
 } // AnnotationSetImpl

Modified: gate/trunk/src/main/gate/corpora/DocumentStaxUtils.java
===================================================================
--- gate/trunk/src/main/gate/corpora/DocumentStaxUtils.java     2013-12-14 
09:08:07 UTC (rev 17172)
+++ gate/trunk/src/main/gate/corpora/DocumentStaxUtils.java     2013-12-15 
18:54:25 UTC (rev 17173)
@@ -15,6 +15,23 @@
  */
 package gate.corpora;
 
+import gate.Annotation;
+import gate.AnnotationSet;
+import gate.Document;
+import gate.DocumentContent;
+import gate.Factory;
+import gate.FeatureMap;
+import gate.Gate;
+import gate.TextualDocument;
+import gate.event.StatusListener;
+import gate.relations.Relation;
+import gate.relations.RelationSet;
+import gate.relations.SimpleRelation;
+import gate.util.GateException;
+import gate.util.GateRuntimeException;
+import gate.util.InvalidOffsetException;
+import gate.util.Out;
+
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -46,20 +63,6 @@
 import javax.xml.stream.XMLStreamReader;
 import javax.xml.stream.XMLStreamWriter;
 
-import gate.Annotation;
-import gate.AnnotationSet;
-import gate.Document;
-import gate.DocumentContent;
-import gate.Factory;
-import gate.FeatureMap;
-import gate.Gate;
-import gate.TextualDocument;
-import gate.event.StatusListener;
-import gate.util.GateException;
-import gate.util.GateRuntimeException;
-import gate.util.InvalidOffsetException;
-import gate.util.Out;
-
 /**
  * This class provides support for reading and writing GATE XML format
  * using StAX (the Streaming API for XML).
@@ -154,7 +157,7 @@
       // not
       Boolean requireAnnotationIds = null;
       int eventType = xsr.nextTag();
-      while(eventType == XMLStreamConstants.START_ELEMENT) {
+      while(eventType == XMLStreamConstants.START_ELEMENT && 
xsr.getLocalName().equals("AnnotationSet")) {
         xsr.require(XMLStreamConstants.START_ELEMENT, null, "AnnotationSet");
         String annotationSetName = xsr.getAttributeValue(null, "Name");
         AnnotationSet annotationSet = null;
@@ -183,10 +186,43 @@
         numAnnots += annotIdsInSet.size();
         // readAnnotationSet leaves reader positioned on the
         // </AnnotationSet> tag, so nextTag takes us to either the next
-        // <AnnotationSet> or to the </GateDocument>
+        // <AnnotationSet>, a <RelationSet>, or </GateDocument>
         eventType = xsr.nextTag();
       }
 
+      while(eventType == XMLStreamConstants.START_ELEMENT
+              && xsr.getLocalName().equals("RelationSet")) {
+        xsr.require(XMLStreamConstants.START_ELEMENT, null, "RelationSet");
+        String relationSetName = xsr.getAttributeValue(null, "Name");
+        RelationSet relations = null;
+        if(relationSetName == null) {
+          if(statusListener != null) {
+            statusListener
+                    .statusChanged("Reading relation set for default 
annotation set");
+          }
+          relations = doc.getAnnotations().getRelations();
+        } else {
+          if(statusListener != null) {
+            statusListener.statusChanged("Reading relation set for \""
+                    + relationSetName + "\" annotation set");
+          }
+          relations = doc.getAnnotations(relationSetName).getRelations();
+        }
+
+        SortedSet<Integer> relIdsInSet = new TreeSet<Integer>();
+        readRelationSet(xsr, relations, relIdsInSet);
+        if(relIdsInSet.size() > 0
+                && (maxAnnotId == null || relIdsInSet.last().intValue() > 
maxAnnotId
+                        .intValue())) {
+          maxAnnotId = relIdsInSet.last();
+        }
+        numAnnots += relIdsInSet.size();
+        // readAnnotationSet leaves reader positioned on the
+        // </RelationSet> tag, so nextTag takes us to either the next
+        // <RelationSet> or to the </GateDocument>
+        eventType = xsr.nextTag();
+      }
+      
       // check we are on the end document tag
       xsr.require(XMLStreamConstants.END_ELEMENT, null, "GateDocument");
 
@@ -369,7 +405,72 @@
     }
     return requireAnnotationIds;
   }
+  
+  public static void readRelationSet(XMLStreamReader xsr,
+          RelationSet relations, Set<Integer> allAnnotIds)
+          throws XMLStreamException {
+    while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
+      xsr.require(XMLStreamConstants.START_ELEMENT, null, "Relation");
+      String type = xsr.getAttributeValue(null, "Type");
+      String idString = xsr.getAttributeValue(null, "Id");
+      String memberString = xsr.getAttributeValue(null, "Members");
+      
+      if(memberString == null)
+        throw new XMLStreamException("A relation must have members");
+      if (type == null)
+        throw new XMLStreamException("A relation must have a type");
+      if (idString == null)
+        throw new XMLStreamException("A relation must have an id");
+      
+      String[] memberStrings = memberString.split(";");
+      int[] members = new int[memberStrings.length];
+      for(int i = 0; i < members.length; ++i) {
+        members[i] = Integer.parseInt(memberStrings[i]);
+      }
 
+      xsr.nextTag();
+      xsr.require(XMLStreamConstants.START_ELEMENT, null, "UserData");
+
+      // get the string representation of the user data
+      StringBuilder stringRep = new StringBuilder(1024);
+      int eventType;
+      while((eventType = xsr.next()) != XMLStreamConstants.END_ELEMENT) {
+        switch(eventType) {
+          case XMLStreamConstants.CHARACTERS:
+            stringRep.append(xsr.getTextCharacters(), xsr.getTextStart(),
+                    xsr.getTextLength());
+            break;
+
+          case XMLStreamConstants.CDATA:
+            stringRep.append(xsr.getTextCharacters(), xsr.getTextStart(),
+                    xsr.getTextLength());
+            break;
+
+          case XMLStreamConstants.START_ELEMENT:
+            throw new XMLStreamException("Elements not allowed within "
+                    + "user data.", xsr.getLocation());
+
+          default:
+            // do nothing - ignore comments, PIs, etc.
+        }
+      }
+
+      xsr.require(XMLStreamConstants.END_ELEMENT, null, "UserData");
+
+      FeatureMap features = readFeatureMap(xsr);
+
+      Relation r = new SimpleRelation(Integer.valueOf(idString), type, 
members);
+      r.setFeatures(features);
+
+      if(stringRep.length() > 0) {
+        ObjectWrapper wrapper = new ObjectWrapper(stringRep.toString());
+        r.setUserData(wrapper.getValue());
+      }
+
+      relations.addRelation(r);
+    }
+  }
+
   /**
    * Processes the TextWithNodes element from this XMLStreamReader,
    * returning the text content of the document. The supplied map is
@@ -964,6 +1065,13 @@
         newLine(xsw);
       }// End if
     }// End while
+    
+    iter = annotationSets.keySet().iterator();
+    while(iter.hasNext()) {
+      
+      writeRelationSet(doc.getAnnotations(iter.next()).getRelations(), xsw,
+              namespaceURI);
+    }
 
     // close the GateDocument element
     xsw.writeEndElement();
@@ -1046,7 +1154,60 @@
     xsw.writeEndElement();
     newLine(xsw);
   }
+  
+  public static void writeRelationSet(RelationSet relations,
+          XMLStreamWriter xsw, String namespaceURI) throws XMLStreamException {
 
+    // if there are no relations then don't write the set, this means
+    // that docs without relations will remain compatible with earlier
+    // versions of GATE
+    if(relations == null || relations.size() == 0) return;
+
+    xsw.writeComment(" Relation Set for "
+            + relations.getAnnotationSet().getName() + " ");
+    newLine(xsw);
+    newLine(xsw);
+
+    xsw.writeStartElement(namespaceURI, "RelationSet");
+
+    if(relations.getAnnotationSet().getName() != null) {
+      xsw.writeAttribute("Name", relations.getAnnotationSet().getName());
+    }
+    newLine(xsw);
+
+    for(Relation relation : relations.get()) {
+
+      StringBuilder str = new StringBuilder();
+      int[] members = relation.getMembers();
+      for(int i = 0; i < members.length; i++) {
+        if(i > 0) str.append(";");
+        str.append(members[i]);
+      }
+      xsw.writeStartElement(namespaceURI, "Relation");
+      xsw.writeAttribute("Id", String.valueOf(relation.getId()));
+      xsw.writeAttribute("Type", relation.getType());
+      xsw.writeAttribute("Members", str.toString());
+      newLine(xsw);
+
+      xsw.writeStartElement(namespaceURI, "UserData");
+      if(relation.getUserData() != null) {
+        ObjectWrapper userData = new ObjectWrapper(relation.getUserData());
+        writeCharactersOrCDATA(xsw,
+                replaceXMLIllegalCharactersInString(userData.toString()));
+      }
+      xsw.writeEndElement();
+      newLine(xsw);
+
+      writeFeatures(relation.getFeatures(), xsw, namespaceURI);
+      xsw.writeEndElement();
+      newLine(xsw);
+    }
+
+    // end RelationSet element
+    xsw.writeEndElement();
+    newLine(xsw);
+  }
+
   /**
    * Retained for binary compatibility, new code should call the
    * <code>Collection&lt;Annotation&gt;</code> version instead.

Modified: gate/trunk/src/main/gate/creole/annotdelete/AnnotationDeletePR.java
===================================================================
--- gate/trunk/src/main/gate/creole/annotdelete/AnnotationDeletePR.java 
2013-12-14 09:08:07 UTC (rev 17172)
+++ gate/trunk/src/main/gate/creole/annotdelete/AnnotationDeletePR.java 
2013-12-15 18:54:25 UTC (rev 17173)
@@ -122,6 +122,9 @@
           } else {
             removeSubSet(document.getAnnotations(), matchesMap);
           }
+          
+          //empty the relation set associated with the annotation set
+          document.getAnnotations().getRelations().clear();
         } else {
           // remove this named set
           if (annotationTypes == null || annotationTypes.isEmpty()) {
@@ -157,6 +160,8 @@
         } else {
           removeSubSet(document.getAnnotations(), matchesMap);
         }
+        //empty the relation set associated with the annotation set
+        document.getAnnotations().getRelations().clear();
       }
 
       //get the names of all sets

Added: gate/trunk/src/main/gate/gui/docview/RelationSetView.java
===================================================================
--- gate/trunk/src/main/gate/gui/docview/RelationSetView.java                   
        (rev 0)
+++ gate/trunk/src/main/gate/gui/docview/RelationSetView.java   2013-12-15 
18:54:25 UTC (rev 17173)
@@ -0,0 +1,94 @@
+/*
+ *  RelationSetView.java
+ *
+ *  Copyright (c) 1995-2013, The University of Sheffield. See the file
+ *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
+ *
+ *  This file is part of GATE (see http://gate.ac.uk/), and is free
+ *  software, licenced under the GNU Library General Public License,
+ *  Version 2, June 1991 (in the distribution as file licence.html,
+ *  and also available at http://gate.ac.uk/gate/licence.html).
+ *
+ *  Mark A. Greenwood, 15th December 2013
+ */
+package gate.gui.docview;
+
+import gate.Document;
+import gate.Resource;
+import gate.creole.AbstractVisualResource;
+import gate.creole.metadata.CreoleResource;
+import gate.creole.metadata.GuiType;
+import gate.gui.MainFrame;
+import gate.relations.RelationSet;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JTextPane;
+import javax.swing.JToolBar;
+
+@CreoleResource(name = "Relation Viewer", guiType = GuiType.LARGE, 
resourceDisplayed = "gate.Document")
+public class RelationSetView extends AbstractVisualResource {
+
+  private static final long serialVersionUID = 2976754146115707386L;
+
+  private JTextPane text = new JTextPane();
+  
+  private Document doc = null;
+
+  @Override
+  public Resource init() {
+    setLayout(new BorderLayout());
+
+    text.setEditable(false);
+
+    add(text, BorderLayout.CENTER);
+    
+    JButton btnRefresh = new JButton("Refresh",MainFrame.getIcon("Refresh"));
+    btnRefresh.addActionListener(new ActionListener() {
+      
+      @Override
+      public void actionPerformed(ActionEvent arg0) {
+        refresh();
+      }
+    });
+    
+    JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
+    toolbar.setFloatable(false);
+    
+    toolbar.add(btnRefresh);
+    toolbar.addSeparator();
+    toolbar.add(new JLabel("Currently this view is not automatically 
updated"));
+    
+    add(toolbar, BorderLayout.NORTH);
+
+    return this;
+  }
+  
+  private void refresh() {
+    StringBuilder builder = new StringBuilder();
+    
+    RelationSet relations = doc.getAnnotations().getRelations();
+    if(relations.size() > 0) {
+      builder.append(relations).append("\n\n");
+    }
+    
+    for(String name : doc.getAnnotationSetNames()) {
+      relations = doc.getAnnotations(name).getRelations();
+      if(relations.size() > 0) {
+        builder.append(name).append(":\n").append(relations).append("\n\n");
+      }
+    }
+
+    text.setText(builder.toString());
+  }
+
+  @Override
+  public void setTarget(Object target) {    
+    doc = (Document)target;
+    refresh();
+  }
+}

Modified: gate/trunk/src/main/gate/relations/Relation.java
===================================================================
--- gate/trunk/src/main/gate/relations/Relation.java    2013-12-14 09:08:07 UTC 
(rev 17172)
+++ gate/trunk/src/main/gate/relations/Relation.java    2013-12-15 18:54:25 UTC 
(rev 17173)
@@ -15,42 +15,48 @@
  */
 package gate.relations;
 
+import gate.util.FeatureBearer;
+import gate.util.IdBearer;
+
 import java.io.Serializable;
 
 /**
  * Interface representing a relation between GATE annotations.
  */
-public interface Relation extends Serializable {
-  
+public interface Relation extends Serializable, IdBearer, FeatureBearer {
+
   /**
    * Get the type of the relation (e.g. {@link #COREF}).
-   * @return the relation type. 
+   * 
+   * @return the relation type.
    */
   public String getType();
-  
+
   /**
-   * Gets the members of the relation. 
+   * Gets the members of the relation.
+   * 
    * @return an array containing annotation IDs.
    */
   public int[] getMembers();
-  
+
   /**
-   * Some relations may have associated arbitrary data; this method can be 
used 
-   * to retrieve it.
-   * @return the user data that was previously added to this relation.
+   * Gets the arbitrary data associated with this relation
+   * 
+   * @return the arbitrary data associated with this relation
    */
-  public Serializable getUserData();
-  
+  public Object getUserData();
+
   /**
-   * Associates some arbitrary user data to this relation.
-   * @param data the user data value.
+   * Sets the arbitrary data associated with this relation
+   * 
+   * @param data the arbitrary data associated with this
+   *          relation
    */
-  public void setUserData(Serializable data);
-  
-  
+  public void setUserData(Object data);
+
   /**
-   * Relation type for co-reference relations. 
+   * Relation type for co-reference relations.
    */
   public static final String COREF = "coref";
-  
+
 }

Modified: gate/trunk/src/main/gate/relations/RelationSet.java
===================================================================
--- gate/trunk/src/main/gate/relations/RelationSet.java 2013-12-14 09:08:07 UTC 
(rev 17172)
+++ gate/trunk/src/main/gate/relations/RelationSet.java 2013-12-15 18:54:25 UTC 
(rev 17173)
@@ -15,15 +15,14 @@
  */
 package gate.relations;
 
+import gate.Annotation;
 import gate.AnnotationSet;
-import gate.Document;
-import gate.Gate;
+import gate.corpora.DocumentImpl;
 
 import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -32,142 +31,147 @@
 import org.apache.log4j.Logger;
 
 /**
- * Utility class for managing a set of GATE relations (usually each annotation
- * set of a document will have one set of associated relations).
+ * Utility class for managing a set of GATE relations (usually each
+ * annotation set of a document will have one set of associated
+ * relations).
  */
 public class RelationSet implements Serializable {
-  
+
   private static final long serialVersionUID = 8552798130184595465L;
 
   private static final Logger log = Logger.getLogger(RelationSet.class);
-  
+
   /**
-   * Annotation ID used when calling {@link #getRelations(int...)} for 
positions
-   * with no restrictions.
+   * Annotation ID used when calling {@link #getRelations(int...)} for
+   * positions with no restrictions.
    */
   public static final int ANY = -1;
-  
+
   /**
-   * Name used for a {@link Document} feature holding a {@link Map} from 
-   * {@link String} name to {@link RelationSet}. By convention, the relation 
set
-   * associated with an {@link AnnotationSet} used the annotation set name as 
a 
-   * key.
-   */
-  public static final String RELATIONS_MAP_DOCUMENT_FEATURE_NAME = 
"gate.relations";
-  
-  /**
    * The list of all relations.
    */
   protected List<Relation> relations;
-  
+
   /**
    * Index for relations by type.
    */
   protected Map<String, BitSet> indexByType;
-  
+
   /**
-   * Keeps the indexes (in {@link #relations}) for relations that have been 
-   * deleted.
+   * Index for relations by id.
    */
+  protected Map<Integer, Relation> indexById;
+
+  /**
+   * Keeps the indexes (in {@link #relations}) for relations that have
+   * been deleted.
+   */
   protected BitSet deleted;
-  
+
   /**
-   * Indexes for relations by member. Each element in the list refers to a 
given
-   * position in the members array: the element at position zero refers to the 
-   * first member of all relations.
+   * Indexes for relations by member. Each element in the list refers to
+   * a given position in the members array: the element at position zero
+   * refers to the first member of all relations.
    * 
-   * The element at position <code>pos</code> is a map from annotation ID 
-   * (representing a relation member) to a {@link BitSet} indicating which of
-   * the relation indexes (in {@link #relations}) correspond to relations that 
-   * contain the given annotation (i.e. member) on the position 
-   * <code>pos</code>. 
+   * The element at position <code>pos</code> is a map from annotation
+   * ID (representing a relation member) to a {@link BitSet} indicating
+   * which of the relation indexes (in {@link #relations}) correspond to
+   * relations that contain the given annotation (i.e. member) on the
+   * position <code>pos</code>.
    */
   protected List<Map<Integer, BitSet>> indexesByMember;
-  
+
   /**
-   * Factory method that gets a named {@link RelationSet} from a document. 
-   * {@link RelationSet}s are stored inside a special document feature, which 
is
-   * where this method retrieves them from. If no relation set is associated
-   * with the given name, then a new one is created and returned.
-   * 
-   * To get the relation set associated with a given annotation set use 
-   * {@link #getRelations(AnnotationSet)} instead.
-   * 
-   * @param document the document for which the relations are being requested.
-   * @param name the name for the relation set. This can be any arbitrary 
-   * {@link String} or even <code>null</code>. However, by convention, names 
of 
-   * annotation sets (and <code>null</code> for the default annotation set) are
-   * used to name the <i>principal</i> relation set for each annotation set.   
-   * @return the {@link RelationSet} requested. 
+   * The {@link AnnotationSet} this set of relations relates to. The
+   * assumption (which is not currently enforced) is that all members of
+   * this RelationSet will be either {@link Annotation} instances from
+   * this {@link AnnotationSet} or other {@link Relation} instances
+   * within this set.
    */
-  public static RelationSet getRelations(Document document, String name) {
-    Object relMapObj = document.getFeatures().get(
-      RELATIONS_MAP_DOCUMENT_FEATURE_NAME);
-    Map<String, RelationSet> relationsMap = null;
-    if(relMapObj != null) {
-      if(relMapObj instanceof Map) {
-        relationsMap = (Map)relMapObj;
-      } else {
-        document.getFeatures().remove(RELATIONS_MAP_DOCUMENT_FEATURE_NAME);
-        log.warn("Invalid value for feature \"" + 
-            RELATIONS_MAP_DOCUMENT_FEATURE_NAME + "\" on document \"" + 
-            document.getName() + "\" has been removed.");
-      }      
-    }
-    if(relationsMap == null) {
-      relationsMap = new HashMap<String, RelationSet>();
-      document.getFeatures().put(RELATIONS_MAP_DOCUMENT_FEATURE_NAME, 
-        relationsMap);
-    }
-    RelationSet relSet = relationsMap.get(name);
-    if(relSet == null) {
-      relSet = new RelationSet();
-      relationsMap.put(name, relSet);
-    }
-    return relSet;
+  protected AnnotationSet annSet;
+
+  public AnnotationSet getAnnotationSet() {
+    return annSet;
   }
-  
+
+  public Collection<Relation> get() {
+    return indexById.values();
+  }
+
   /**
-   * Factory method that gets the {@link RelationSet} associated with an 
-   * {@link AnnotationSet}.
-   * @param annSet the annotation set for which the relations are being 
-   * requested.
-   * @return the requested relation set. If none exists, a new one is created
-   * and returned.
+   * You should never create a RelationSet directly, instead get if via
+   * the AnnotationSet
    */
-  public static RelationSet getRelations(AnnotationSet annSet) {
-    return getRelations(annSet.getDocument(), annSet.getName());  
-  }
-  
-  protected RelationSet() {
+  public RelationSet(AnnotationSet annSet) {
+    this.annSet = annSet;
     relations = new ArrayList<Relation>();
     indexByType = new HashMap<String, BitSet>();
-    indexesByMember = new ArrayList<Map<Integer,BitSet>>();
+    indexesByMember = new ArrayList<Map<Integer, BitSet>>();
+    indexById = new HashMap<Integer, Relation>();
     deleted = new BitSet();
   }
+  
+  /**
+   * Empties the relation set
+   */
+  public void clear() {
+    relations.clear();
+    indexByType.clear();
+    indexesByMember.clear();
+    indexById.clear();
+    deleted.clear();
+  }
 
   /**
-   * Creates a new {@link Relation} and adds it to this set. Uses the default 
-   * relation implementation at {@link SimpleRelation}.
+   * The number of relations in this set.
+   * 
+   * @return the number of relations in this set.
+   */
+  public int size() {
+    // return the size of the by ID index as this is the only place that
+    // actually tracks the size accurately once a relation has been
+    // deleted from the set
+    return indexById.size();
+  }
+
+  /**
+   * Creates a new {@link Relation} and adds it to this set. Uses the
+   * default relation implementation at {@link SimpleRelation}.
+   * 
    * @param type the type for the new relation.
-   * @param members the annotation IDs for the annotations that are members in 
-   * this relation.
+   * @param members the annotation IDs for the annotations that are
+   *          members in this relation.
    * @return the newly created {@link Relation} instance.
    */
-  public Relation addRelation(String type, int... members) {
-    Relation rel = new SimpleRelation(type, members);
+  public Relation addRelation(String type, int... members)
+          throws IllegalArgumentException {
+
+    if(members.length == 0)
+      throw new IllegalArgumentException("A relation can't have zero members");
+
+    for(int member : members) {
+      if(!indexById.containsKey(member) && annSet.get(member) == null)
+        throw new IllegalArgumentException(
+                "Member must be from within this annotation set");
+    }
+
+    Relation rel =
+            new SimpleRelation(
+                    ((DocumentImpl)annSet.getDocument()).getNextAnnotationId(),
+                    type, members);
     addRelation(rel);
     return rel;
   }
-  
-  
+
   /**
    * Adds an externally-created {@link Relation} instance.
+   * 
    * @param rel the {@link Relation} to be added.
    */
   public void addRelation(Relation rel) {
     int relIdx = relations.size();
     relations.add(rel);
+    indexById.put(rel.getId(), rel);
     BitSet sameType = indexByType.get(rel.getType());
     if(sameType == null) {
       sameType = new BitSet(relations.size());
@@ -190,45 +194,53 @@
       sameMember.set(relIdx);
     }
   }
-  
+
   /**
-   * Returns the maximum arity for any relation in this {@link RelationSet}.
+   * Returns the maximum arity for any relation in this
+   * {@link RelationSet}.
+   * 
    * @return an int value.
    */
   public int getMaximumArity() {
     return indexesByMember.size();
   }
-  
+
   /**
    * Finds relations based on their type.
+   * 
    * @param type the type of relation being sought.
-   * @return the list of all relations in this {@link RelationSet} that have 
the
-   * required type.
+   * @return the list of all relations in this {@link RelationSet} that
+   *         have the required type.
    */
   public List<Relation> getRelations(String type) {
     List<Relation> res = new ArrayList<Relation>();
     BitSet rels = indexByType.get(type);
     if(rels != null) {
       rels.andNot(deleted);
-      for(int relPos = 0; relPos < relations.size(); relPos++){
-        if(rels.get(relPos)) res.add(relations.get(relPos));  
+      for(int relPos = 0; relPos < relations.size(); relPos++) {
+        if(rels.get(relPos)) res.add(relations.get(relPos));
       }
     }
     return res;
   }
-  
+
+  public Relation get(Integer id) {
+    return indexById.get(id);
+  }
+
   /**
    * Finds relations based on their members.
-   * @param members an array containing annotation IDs. If a constraint is not
-   * required for a given member position, then the {@link #ANY}. value should 
-   * be used.
-   * @return all the relations that have the given annotation IDs (members)
-   * on the specified positions.
+   * 
+   * @param members an array containing annotation IDs. If a constraint
+   *          is not required for a given member position, then the
+   *          {@link #ANY}. value should be used.
+   * @return all the relations that have the given annotation IDs
+   *         (members) on the specified positions.
    */
   public List<Relation> getRelations(int... members) {
     // get the lists of relations for each member
     BitSet[] postingLists = new BitSet[indexesByMember.size()];
-    for(int  i = 0; i < postingLists.length; i++) {
+    for(int i = 0; i < postingLists.length; i++) {
       if(i < members.length && members[i] >= 0) {
         postingLists[i] = indexesByMember.get(i).get(members[i]);
       } else {
@@ -237,11 +249,11 @@
     }
     return intersection(postingLists);
   }
-  
+
   public List<Relation> getRelations(String type, int... members) {
     // get the lists of relations for each member
     BitSet[] postingLists = new BitSet[indexesByMember.size() + 1];
-    for(int  i = 0; i < postingLists.length; i++) {
+    for(int i = 0; i < postingLists.length; i++) {
       if(i < members.length && members[i] >= 0) {
         postingLists[i] = indexesByMember.get(i).get(members[i]);
       } else {
@@ -251,28 +263,33 @@
     postingLists[postingLists.length - 1] = indexByType.get(type);
     return intersection(postingLists);
   }
-  
+
   /**
    * Deletes the specified relation.
+   * 
    * @param relation the relation to be deleted.
-   * @return <code>true</code> if the given relation was deleted, or 
-   * <code>false</code> if it was not found.
+   * @return <code>true</code> if the given relation was deleted, or
+   *         <code>false</code> if it was not found.
    */
   public boolean deleteRelation(Relation relation) {
     int relIdx = relations.indexOf(relation);
-    if(relIdx >= 0){
+    if(relIdx >= 0) {
       deleted.set(relIdx);
       relations.set(relIdx, null);
+      indexById.remove(relation.getId());
       return true;
     } else {
       return false;
     }
   }
-  
+
   /**
-   * Calculates the intersection of a set of lists containing relation indexes.
+   * Calculates the intersection of a set of lists containing relation
+   * indexes.
+   * 
    * @param indexLists the list to be intersected.
-   * @return the list of relations contained in all the supplied index lists. 
+   * @return the list of relations contained in all the supplied index
+   *         lists.
    */
   protected List<Relation> intersection(BitSet... indexLists) {
     BitSet relIds = new BitSet(relations.size());
@@ -284,15 +301,17 @@
         if(relIds.isEmpty()) break;
       }
     }
-    
+
     List<Relation> res = new ArrayList<Relation>();
-    for(int relIdx = 0; relIdx < relations.size(); relIdx++){
+    for(int relIdx = 0; relIdx < relations.size(); relIdx++) {
       if(relIds.get(relIdx)) res.add(relations.get(relIdx));
     }
     return res;
   }
 
-  /* (non-Javadoc)
+  /*
+   * (non-Javadoc)
+   * 
    * @see java.lang.Object#toString()
    */
   @Override
@@ -300,17 +319,17 @@
     StringBuilder str = new StringBuilder();
     str.append("[");
     boolean first = true;
-    for(int  i = 0; i < relations.size(); i++) {
+    for(int i = 0; i < relations.size(); i++) {
       if(!deleted.get(i)) {
         if(first) {
           first = false;
-        } else{ 
+        } else {
           str.append("; ");
         }
         String relStr = relations.get(i).toString();
         relStr = relStr.replaceAll(";", Matcher.quoteReplacement("\\;"));
         if(!relations.get(i).getClass().equals(SimpleRelation.class)) {
-          relStr = "(" + relations.get(i).getClass().getName() + ")" + relStr; 
 
+          relStr = "(" + relations.get(i).getClass().getName() + ")" + relStr;
         }
         str.append(relStr);
       }
@@ -318,5 +337,4 @@
     str.append("]");
     return str.toString();
   }
-  
 }

Modified: gate/trunk/src/main/gate/relations/SimpleRelation.java
===================================================================
--- gate/trunk/src/main/gate/relations/SimpleRelation.java      2013-12-14 
09:08:07 UTC (rev 17172)
+++ gate/trunk/src/main/gate/relations/SimpleRelation.java      2013-12-15 
18:54:25 UTC (rev 17173)
@@ -15,7 +15,9 @@
  */
 package gate.relations;
 
-import java.io.Serializable;
+import gate.Factory;
+import gate.FeatureMap;
+
 import java.util.Arrays;
 import java.util.regex.Matcher;
 
@@ -23,23 +25,36 @@
  * A simple implementation for the {@link Relation} interface.
  */
 public class SimpleRelation implements Relation {
-  
+
   private static final long serialVersionUID = 6866132107461267866L;
 
   protected String type;
-  
+
   protected int[] members;
-  
-  protected Serializable userData;
-  
-  public SimpleRelation(String type, int[] members) {
+
+  protected int id;
+
+  protected FeatureMap features;
+
+  protected Object userData;
+
+  /**
+   * You should never create instances of this class directly, you
+   * should create new relations via the appropriate methods of
+   * {@link RelationSet}. This method is only publicly available to
+   * support persistence.
+   */
+  public SimpleRelation(int id, String type, int[] members) {
     super();
+    this.id = id;
     this.type = type;
     this.members = members;
+    features = Factory.newFeatureMap();
   }
-  
 
-  /* (non-Javadoc)
+  /*
+   * (non-Javadoc)
+   * 
    * @see gate.relations.Relation#getType()
    */
   @Override
@@ -47,7 +62,9 @@
     return type;
   }
 
-  /* (non-Javadoc)
+  /*
+   * (non-Javadoc)
+   * 
    * @see gate.relations.Relation#getMembers()
    */
   @Override
@@ -55,65 +72,53 @@
     return members;
   }
 
-  /* (non-Javadoc)
-   * @see gate.relations.Relation#getUserData()
-   */
-  @Override
-  public Serializable getUserData() {
-    return userData;
-  }
-
-  /* (non-Javadoc)
-   * @see gate.relations.Relation#setUserData(java.io.Serializable)
-   */
-  @Override
-  public void setUserData(Serializable data) {
-    this.userData = data;
-  }
-
-  /* (non-Javadoc)
+  /*
+   * (non-Javadoc)
+   * 
    * @see java.lang.Object#toString()
    */
   @Override
   public String toString() {
     StringBuilder str = new StringBuilder();
-    String typeOut = type.replaceAll("\\(", 
-        Matcher.quoteReplacement("\\(")).replaceAll("\\)", 
-        Matcher.quoteReplacement("\\)"));
+    str.append(id).append(": ");
+    String typeOut =
+            type.replaceAll("\\(", Matcher.quoteReplacement("\\(")).replaceAll(
+                    "\\)", Matcher.quoteReplacement("\\)"));
     str.append(typeOut).append("(");
     for(int i = 0; i < members.length; i++) {
       if(i > 0) str.append(", ");
       str.append(members[i]);
     }
     str.append(")");
+    if(features != null) {
+      str.append("#").append(features.toString());
+    }
     if(userData != null) {
       str.append("#").append(userData.toString());
     }
     return str.toString();
   }
 
-  /* (non-Javadoc)
-   * @see java.lang.Object#hashCode()
-   */
   @Override
   public int hashCode() {
     final int prime = 31;
     int result = 1;
+    result = prime * result + ((features == null) ? 0 : features.hashCode());
     result = prime * result + Arrays.hashCode(members);
     result = prime * result + ((type == null) ? 0 : type.hashCode());
     result = prime * result + ((userData == null) ? 0 : userData.hashCode());
     return result;
   }
 
-  /* (non-Javadoc)
-   * @see java.lang.Object#equals(java.lang.Object)
-   */
   @Override
   public boolean equals(Object obj) {
     if(this == obj) return true;
     if(obj == null) return false;
-    if(!(obj instanceof SimpleRelation)) return false;
+    if(getClass() != obj.getClass()) return false;
     SimpleRelation other = (SimpleRelation)obj;
+    if(features == null) {
+      if(other.features != null) return false;
+    } else if(!features.equals(other.features)) return false;
     if(!Arrays.equals(members, other.members)) return false;
     if(type == null) {
       if(other.type != null) return false;
@@ -123,6 +128,29 @@
     } else if(!userData.equals(other.userData)) return false;
     return true;
   }
-  
-  
+
+  @Override
+  public Integer getId() {
+    return id;
+  }
+
+  @Override
+  public FeatureMap getFeatures() {
+    return features;
+  }
+
+  @Override
+  public void setFeatures(FeatureMap features) {
+    this.features = features;
+  }
+
+  @Override
+  public Object getUserData() {
+    return userData;
+  }
+
+  @Override
+  public void setUserData(Object data) {
+    userData = data;
+  }
 }

Modified: gate/trunk/src/main/gate/resources/creole/creole.xml
===================================================================
--- gate/trunk/src/main/gate/resources/creole/creole.xml        2013-12-14 
09:08:07 UTC (rev 17172)
+++ gate/trunk/src/main/gate/resources/creole/creole.xml        2013-12-15 
18:54:25 UTC (rev 17173)
@@ -134,6 +134,10 @@
     <RESOURCE>
       <CLASS>gate.gui.ControllerMetadataViewer</CLASS>
     </RESOURCE>
+    
+    <RESOURCE>
+      <CLASS>gate.gui.docview.RelationSetView</CLASS>
+    </RESOURCE>
 
   </CREOLE>
 </CREOLE-DIRECTORY>

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT 
organizations don't have a clear picture of how application performance 
affects their revenue. With AppDynamics, you get 100% visibility into your 
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
GATE-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gate-cvs

Reply via email to