Now using dependency injection for the XslTransformer instead of
[utils] / crawler / kiss / src / org / wamblee / crawler / kiss / main / ProgramActionExecutor.java
1 /*
2  * Copyright 2005 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.wamblee.crawler.kiss.main;
18
19 import java.util.EnumMap;
20 import java.util.HashSet;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.TreeMap;
24 import java.util.TreeSet;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.dom4j.DocumentFactory;
29 import org.dom4j.Element;
30 import org.wamblee.crawler.kiss.guide.Program;
31 import org.wamblee.crawler.kiss.guide.TimeInterval;
32 import org.wamblee.crawler.kiss.guide.Program.RecordingResult;
33
34 /**
35  * Provides execution of actions for programs. Actions use this class to tell
36  * the executor what to do. The executor then decides on exactly what to do and
37  * in what order and makes decisions in case of conflicts.
38  */
39 public class ProgramActionExecutor {
40
41     private static final Log LOG = LogFactory
42             .getLog(ProgramActionExecutor.class);
43
44     /**
45      * A map of category name to a set of program. Useful for displaying the
46      * output of possibly interesting programs on a per category basis.
47      */
48     private Map<String, Set<Program>> _interestingShows;
49
50     /**
51      * Map of priority to set of programs.
52      */
53     private Map<Integer, Set<Program>> _showsToRecord;
54
55     /**
56      * Map or recording result to a set of programs.
57      */
58     private EnumMap<RecordingResult, Set<Program>> _recordings;
59
60     /**
61      * Constructs the program action executor.
62      * 
63      */
64     public ProgramActionExecutor() {
65         _interestingShows = new TreeMap<String, Set<Program>>();
66         _showsToRecord = new TreeMap<Integer, Set<Program>>();
67         _recordings = new EnumMap<RecordingResult, Set<Program>>(
68                 RecordingResult.class);
69         for (RecordingResult result : RecordingResult.values()) {
70             _recordings.put(result, new TreeSet<Program>(
71                     new Program.TimeSorter()));
72         }
73     }
74
75     /**
76      * Called by an action to indicate the desire to record a program.
77      * 
78      * @param aPriority
79      *            Priority of the program. Used to resolve conflicts.
80      * @param aProgram
81      *            Program to record.
82      */
83     public void recordProgram(int aPriority, Program aProgram) {
84         LOG.info("priority = " + aPriority + ", program: " + aProgram);
85         // Putting -priority into the set makes sure that iteration order 
86         // over the priorities will go from higher priority to lower priority. 
87         Set<Program> programs = _showsToRecord.get(-aPriority);
88         if (programs == null) {
89             programs = new TreeSet<Program>(new Program.TimeSorter());
90             _showsToRecord.put(-aPriority, programs); 
91         }
92         programs.add(aProgram);
93     }
94
95     /**
96      * Called by an action to indicate that a program is interesting.
97      * 
98      * @param aCategory
99      *            Category of the program.
100      * @param aProgram
101      *            Program.
102      */
103     public void interestingProgram(String aCategory, Program aProgram) {
104         LOG.info("category = '" + aCategory + "', program: " + aProgram);
105         Set<Program> programs = _interestingShows.get(aCategory);
106         if (programs == null) {
107             programs = new TreeSet<Program>(new Program.TimeSorter());
108             _interestingShows.put(aCategory, programs);
109         }
110         programs.add(aProgram);
111     }
112
113     /**
114      * Makes sure that the actions are performed.
115      */
116     public void commit() {
117         Set<TimeInterval> previouslyRecorded = new HashSet<TimeInterval>();
118         for (Integer priority : _showsToRecord.keySet()) {
119             for (Program program : _showsToRecord.get(priority)) {
120                 TimeInterval interval = program.getInterval(); 
121                 if ( recordingConflictExists(previouslyRecorded, interval)) { 
122                     _recordings.get(RecordingResult.CONFLICT).add(program);
123                 } else {
124                     RecordingResult result = program.record();
125                     _recordings.get(result).add(program);
126                     previouslyRecorded.add(interval);
127                 }
128             }
129         }
130     }
131     
132     /**
133      * Checks an interval for overlap with a previously recorded program. 
134      * @param aPreviouslyRecorded Previously recorded programs. 
135      * @param interval Interval. 
136      * @return True iff there is a recording conflict.
137      */
138     private boolean recordingConflictExists(Set<TimeInterval> aPreviouslyRecorded, TimeInterval interval) { 
139         for (TimeInterval recordedInterval: aPreviouslyRecorded ) { 
140             if ( interval.overlap(recordedInterval)) {
141                 return true; 
142             }
143         }
144         return false; 
145     }
146
147     /**
148      * Get report as XML.
149      * 
150      * @return XML report
151      */
152     public Element getReport() {
153         DocumentFactory factory = DocumentFactory.getInstance();
154         Element report = factory.createElement("report");
155         
156         Set<Program> reportedPrograms = new HashSet<Program>();
157
158         for (RecordingResult result : RecordingResult.values()) {
159             if (_recordings.get(result).size() > 0) {
160                 Element recordingResult = report.addElement("recorded")
161                         .addAttribute("result", result.toString());
162
163                 for (Program program : _recordings.get(result)) {
164                     recordingResult.add(program.asXml());
165                     reportedPrograms.add(program);
166                 }
167             }
168         }
169
170         if (_interestingShows.size() > 0) {
171             Element interesting = report.addElement("interesting");
172             for (String category : _interestingShows.keySet()) {
173                 Element categoryElem = interesting;
174                 if (category.length() > 0) {
175                     categoryElem = interesting.addElement("category");
176                     categoryElem.addAttribute("name", category);
177                 }
178                 for (Program program : _interestingShows.get(category)) {
179                     if ( !reportedPrograms.contains(program)) {
180                         categoryElem.add(program.asXml());
181                     } else { 
182                         LOG.info("Category '" + category + "', program " + program + " already reported");
183                     }
184                 }
185                 if ( categoryElem.elements().size() == 0 ) {
186                     // Remove empty category element. 
187                     LOG.info("Removing element for category '" + category + "'");
188                     interesting.remove(categoryElem);
189                 }
190             }
191
192         }
193
194         return report;
195     }
196 }