Add ability to load previously found bugs back out of the database.
[cfb.git] / prod / net / jaekl / cfb / xml / BugInstance.java
index 100251f9e9a37fce9235b30beb1492ebec24adc3..96a815e5aaa41089beafd6599878a83d0e922e3a 100644 (file)
@@ -2,11 +2,13 @@ package net.jaekl.cfb.xml;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
 import org.xml.sax.Attributes;
 
+import net.jaekl.cfb.store.Location;
 import net.jaekl.cfb.util.Util;
 import net.jaekl.qd.xml.MissingAttributeException;
 import net.jaekl.qd.xml.ParseResult;
@@ -23,35 +25,61 @@ public class BugInstance extends ParseResult {
        static final String CATEGORY = "category";
        static final String TYPE = "type";
 
+       Long m_id;
        String m_category;
        String m_type;
        ArrayList<BugClass> m_classes;
        ArrayList<BugMethod> m_methods;
        ArrayList<LocalVariable> m_locals;
        ArrayList<SourceLine> m_lines;
+       ArrayList<Location> m_locations;
        
        public BugInstance() {
                super(TAG, INTERNAL, EXTERNAL);
                
+               m_id = null;
                m_category = m_type = null;
                m_classes = new ArrayList<BugClass>();
                m_methods = new ArrayList<BugMethod>();
                m_locals = new ArrayList<LocalVariable>();
                m_lines = new ArrayList<SourceLine>();
+               m_locations = new ArrayList<Location>();
+       }
+       
+       public BugInstance(Long id,
+                                  String category, 
+                                  String type,
+                                  Location[] locations,
+                                  LocalVariable[] variables)
+       {
+               super(TAG, INTERNAL, EXTERNAL);
+               
+               m_id = id;
+               m_category = category;
+               m_type = type;
+               m_classes = null;
+               m_methods = null;
+               m_lines = null;
+               m_locations = new ArrayList<Location>(Arrays.asList(locations));
+               m_locals = new ArrayList<LocalVariable>(Arrays.asList(variables));
        }
        
        public String getCategory() { return m_category; }
        public String getType() { return m_type; }
-       public List<BugClass> getClasses() { return Collections.unmodifiableList(m_classes); }
-       public List<BugMethod> getMethods() { return Collections.unmodifiableList(m_methods); }
-       public List<SourceLine> getLines() { return Collections.unmodifiableList(m_lines); }
        public List<LocalVariable> getVariables() { return Collections.unmodifiableList(m_locals); }
+       public List<Location> getLocations() { return Collections.unmodifiableList(m_locations); }
        
        @Override
        public void endContents(String uri, String localName, String qName, String chars) 
                throws XmlParseException 
        {
-               // no-op
+               // no operation
+       }
+       
+       @Override
+       public void complete()
+       {
+               computeLocations();
        }
 
        @Override
@@ -144,18 +172,18 @@ public class BugInstance extends ParseResult {
                                return false;
                        }
 
-                       BugMethod thisMethod = this.getPrincipalMethod();
-                       BugMethod thatMethod = that.getPrincipalMethod();
-                       if (null == thisMethod) {
-                               if (null == thatMethod) {
+                       Location thisLoc = this.getPrincipalLocation();
+                       Location thatLoc = that.getPrincipalLocation();
+                       if (null == thisLoc) {
+                               if (null == thatLoc) {
                                        return false;
                                }
                        }
                        else {
-                               if (! Util.objsAreEqual(thisMethod.getClassName(), thatMethod.getClassName())) {
+                               if (! Util.objsAreEqual(thisLoc.getClassName(), thatLoc.getClassName())) {
                                        return false;
                                }
-                               if (! Util.objsAreEqual(thisMethod.getMethodName(), thatMethod.getMethodName())) {
+                               if (! Util.objsAreEqual(thisLoc.getMethodName(), thatLoc.getMethodName())) {
                                        return false;
                                }
                        }
@@ -174,19 +202,77 @@ public class BugInstance extends ParseResult {
        {
                int code = Util.objHashCode(m_type)
                                 * Util.objHashCode(m_category)
-                                * Util.objHashCode(getPrincipalMethod())
+                                * Util.objHashCode(getPrincipalLocation())
                                 * Util.objHashCode(getVariables());
                return code;
        }
        
-       // Get the "principal" method.
+       // Get the "principal" Location.
        // This should be the place where the bug is reported.
-       BugMethod getPrincipalMethod()
+       Location getPrincipalLocation()
        {
-               List<BugMethod> bugMethods = getMethods();
-               if ((null == bugMethods) || (0 == bugMethods.size())) {
-                       return null;
+               if (null != m_locations && m_locations.size() > 0) {
+                       return m_locations.get(0);
+               }
+               return null;
+       }
+       
+       private void computeLocations()
+       {
+               assert(null != m_classes);
+               assert(null != m_methods);
+               assert(null != m_lines);
+               
+               m_locations.clear();
+               
+               /*
+                       Somewhat unfortunate special case.
+                       The primary "location" for a bug instance is split between tags.
+                       Most bugs have a pattern like this:
+                       <BugInstance>
+                               ...
+                               <Method>
+                                       <SourceLine .../>
+                               </Method>
+                               ...
+                               <SourceLine .../>
+                       </BugInstance>
+                       
+                       The primary location for a bug is given by the <Method> with no role attribute, 
+                       but the <SourceLine/> inside that method describes the whole range of lines 
+                       covered by that Method, not the spot where the bug is located--that is given 
+                       by the <SourceLine/> that is a direct child fo the <BugInstance/>.
+                */
+               
+               BugMethod primaryMethod = null;
+               SourceLine primaryLine = null;
+               
+               for (BugMethod method : m_methods) {
+                       if (null != method.getRole()) {
+                               primaryMethod = method;
+                               break;
+                       }
+               }
+               if (m_lines.size() > 0) {
+                       primaryLine = m_lines.get(0);
+               }
+               
+               if ((null != primaryMethod) && (null != primaryLine)) {
+                       m_locations.add(new Location(primaryMethod, primaryLine));
+               }
+               
+               for (BugMethod method : m_methods) {
+                       if (primaryMethod != method) {
+                               m_locations.add(new Location(method));                          
+                       }
+               }
+               for (SourceLine line : m_lines) {
+                       if (primaryLine != line) {
+                               m_locations.add(new Location(line));
+                       }
+               }
+               for (BugClass clazz : m_classes) {
+                       m_locations.add(new Location(clazz));
                }
-               return bugMethods.get(0);
        }
 }