Programs for which there was already an attempt to record them will no
[utils] / crawler / kiss / src / org / wamblee / crawler / kiss / main / ProgramActionExecutor.java
index 3ef511cffe4ce9c8b36c445d163e12c57dd7886e..51aef60f1a2148f58300f1c085b642e4028a322f 100644 (file)
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- */ 
+ */
 
 package org.wamblee.crawler.kiss.main;
 
 import java.util.EnumMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.dom4j.DocumentFactory;
 import org.dom4j.Element;
 import org.wamblee.crawler.kiss.guide.Program;
+import org.wamblee.crawler.kiss.guide.TimeInterval;
 import org.wamblee.crawler.kiss.guide.Program.RecordingResult;
 
 /**
- * Provides execution of actions for programs. Actions use
- * this class to tell the executor what to do. The executor then decide
- * on exactly what to do and in what order and makes decisions in case
- * of conflicts.     
+ * Provides execution of actions for programs. Actions use this class to tell
+ * the executor what to do. The executor then decides on exactly what to do and
+ * in what order and makes decisions in case of conflicts.
  */
 public class ProgramActionExecutor {
-    
+
+    private static final Log LOG = LogFactory
+            .getLog(ProgramActionExecutor.class);
+
     /**
-     * A map of category name to a set of program. Useful for displaying the output of 
-     * possibly interesting programs on a per category basis. 
+     * A map of category name to a set of program. Useful for displaying the
+     * output of possibly interesting programs on a per category basis.
      */
     private Map<String, Set<Program>> _interestingShows;
-    
+
     /**
-     * Set of programs to record. 
+     * Map of priority to set of programs.
      */
-    private Set<Program> _showsToRecord;
-   
+    private Map<Integer, Set<Program>> _showsToRecord;
+
     /**
-     * Map or recording result to a set of programs. 
+     * Map or recording result to a set of programs.
      */
     private EnumMap<RecordingResult, Set<Program>> _recordings;
-    
+
     /**
-     * Constructs the program action executor. 
-     *
+     * Constructs the program action executor.
+     * 
      */
-    public ProgramActionExecutor() { 
-        _interestingShows = new TreeMap<String,Set<Program>>();
-        _showsToRecord = new TreeSet<Program>(new Program.TimeSorter());
+    public ProgramActionExecutor() {
+        _interestingShows = new TreeMap<String, Set<Program>>();
+        _showsToRecord = new TreeMap<Integer, Set<Program>>();
         _recordings = new EnumMap<RecordingResult, Set<Program>>(
                 RecordingResult.class);
         for (RecordingResult result : RecordingResult.values()) {
-            _recordings.put(result, new TreeSet<Program>(new Program.TimeSorter()));
+            _recordings.put(result, new TreeSet<Program>(
+                    new Program.TimeSorter()));
         }
     }
-    
+
     /**
      * Called by an action to indicate the desire to record a program.
-     * @param aPriority Priority of the program. Used to resolve conflicts.  
-     * @param aProgram Program to record. 
+     * 
+     * @param aPriority
+     *            Priority of the program. Used to resolve conflicts.
+     * @param aProgram
+     *            Program to record.
      */
-    public void recordProgram(int aPriority, Program aProgram) { 
-        _showsToRecord.add(aProgram);
+    public void recordProgram(int aPriority, Program aProgram) {
+        LOG.info("priority = " + aPriority + ", program: " + aProgram);
+        // Putting -priority into the set makes sure that iteration order 
+        // over the priorities will go from higher priority to lower priority. 
+        Set<Program> programs = _showsToRecord.get(-aPriority);
+        if (programs == null) {
+            programs = new TreeSet<Program>(new Program.TimeSorter());
+            _showsToRecord.put(-aPriority, programs); 
+        }
+        programs.add(aProgram);
     }
-    
+
     /**
-     * Called by an action to indicate that a program is interesting. 
-     * @param aCategory Category of the program. 
-     * @param aProgram Program. 
+     * Called by an action to indicate that a program is interesting.
+     * 
+     * @param aCategory
+     *            Category of the program.
+     * @param aProgram
+     *            Program.
      */
-    public void interestingProgram(String aCategory, Program aProgram) { 
-        Set<Program> programs = _interestingShows.get(aCategory);   
-        if ( programs == null ) { 
+    public void interestingProgram(String aCategory, Program aProgram) {
+        LOG.info("category = '" + aCategory + "', program: " + aProgram);
+        Set<Program> programs = _interestingShows.get(aCategory);
+        if (programs == null) {
             programs = new TreeSet<Program>(new Program.TimeSorter());
             _interestingShows.put(aCategory, programs);
         }
         programs.add(aProgram);
     }
-    
+
     /**
      * Makes sure that the actions are performed.
-     *
      */
-    public void commit() { 
-        for (Program program: _showsToRecord) { 
-            RecordingResult result = program.record(); 
-            _recordings.get(result).add(program);
+    public void commit() {
+        Set<TimeInterval> previouslyRecorded = new HashSet<TimeInterval>();
+        for (Integer priority : _showsToRecord.keySet()) {
+            for (Program program : _showsToRecord.get(priority)) {
+                TimeInterval interval = program.getInterval(); 
+                if ( recordingConflictExists(previouslyRecorded, interval)) { 
+                    _recordings.get(RecordingResult.CONFLICT).add(program);
+                } else {
+                    RecordingResult result = program.record();
+                    _recordings.get(result).add(program);
+                    previouslyRecorded.add(interval);
+                }
+            }
         }
     }
     
     /**
-     * Gets the report describing what was done. 
-     * @return Report. 
+     * Checks an interval for overlap with a previously recorded program. 
+     * @param aPreviouslyRecorded Previously recorded programs. 
+     * @param interval Interval. 
+     * @return True iff there is a recording conflict.
      */
-    public String getReport() {
-        StringBuffer msg = new StringBuffer("Summary of KiSS crawler: \n\n\n");
-        
-        boolean printed = false; 
-
-        for (RecordingResult result : RecordingResult.values()) {
-            if (_recordings.get(result).size() > 0) {
-                msg.append(result.getDescription() + "\n\n");
-                for (Program program : _recordings.get(result)) {
-                    msg.append(program + "\n\n");
-                    printed = true; 
-                }
+    private boolean recordingConflictExists(Set<TimeInterval> aPreviouslyRecorded, TimeInterval interval) { 
+        for (TimeInterval recordedInterval: aPreviouslyRecorded ) { 
+            if ( interval.overlap(recordedInterval)) {
+                return true; 
             }
         }
-        
-        if ( _interestingShows.size() > 0 ) { 
-            msg.append("Possibly interesting shows:\n\n");
-            for (String category: _interestingShows.keySet()) { 
-                if ( category.length() > 0 ) { 
-                    msg.append("Category: " + category + "\n\n");
-                }
-                for (Program program: _interestingShows.get(category)) { 
-                    msg.append(program + "\n\n");
-                    printed = true; 
-                }
-            }
-        
-        }
-        if (!printed) {
-            msg.append("No suitable programs found");
-        }
-
-        return msg.toString(); 
+        return false; 
     }
-    
+
     /**
-     * Get report as XML. 
-     * @return XML report 
+     * Get report as XML.
+     * 
+     * @return XML report
      */
-    public Element getXmlReport() { 
-        DocumentFactory factory = DocumentFactory.getInstance(); 
-        Element report = factory.createElement("report"); 
+    public Element getReport() {
+        DocumentFactory factory = DocumentFactory.getInstance();
+        Element report = factory.createElement("report");
         
+        Set<Program> reportedPrograms = new HashSet<Program>();
+
         for (RecordingResult result : RecordingResult.values()) {
             if (_recordings.get(result).size() > 0) {
-                Element recordingResult = report.addElement("recorded").addAttribute("result", result.toString());
-         
+                Element recordingResult = report.addElement("recorded")
+                        .addAttribute("result", result.toString());
+
                 for (Program program : _recordings.get(result)) {
-                    recordingResult.add(program.asXml()); 
+                    recordingResult.add(program.asXml());
+                    reportedPrograms.add(program);
                 }
             }
         }
-        
-     
-        if ( _interestingShows.size() > 0 ) { 
+
+        if (_interestingShows.size() > 0) {
             Element interesting = report.addElement("interesting");
-            for (String category: _interestingShows.keySet()) { 
-                Element categoryElem = interesting; 
-                if ( category.length() > 0 ) { 
+            for (String category : _interestingShows.keySet()) {
+                Element categoryElem = interesting;
+                if (category.length() > 0) {
                     categoryElem = interesting.addElement("category");
                     categoryElem.addAttribute("name", category);
                 }
-                for (Program program: _interestingShows.get(category)) {
-                    categoryElem.add(program.asXml()); 
+                for (Program program : _interestingShows.get(category)) {
+                    if ( !reportedPrograms.contains(program)) {
+                        categoryElem.add(program.asXml());
+                    } else { 
+                        LOG.info("Category '" + category + "', program " + program + " already reported");
+                    }
+                }
+                if ( categoryElem.elements().size() == 0 ) {
+                    // Remove empty category element. 
+                    LOG.info("Removing element for category '" + category + "'");
+                    interesting.remove(categoryElem);
                 }
             }
-        
+
         }
-        return report; 
+
+        return report;
     }
 }