public String toString() {
return "PageType(type='" + _type + "')";
}
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PageType)) {
+ return false;
+ }
+ return toString().equals(obj.toString());
+ }
}
public Element getContent() {
return _content;
}
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if ( !(obj instanceof ActionImpl )) {
+ return false;
+ }
+ ActionImpl action = (ActionImpl)obj;
+ return _reference.equals(action._reference) &&
+ _type.equals(action._type);
+ }
}
private static final int MAX_TRIES = 3;
- private static final int MAX_DELAY = 5000;
+ private static final int MAX_DELAY = 100;
private PrintStream _os;
<programs>
<program>
- <name>star.*gate</name>
+ <action>notify</action>
+ <match field="description">horror</match>
</program>
<program>
- <name>battlestar</name>
+ <action>notify</action>
+ <match field="description">((sci-fi)|(science fiction))</match>
+ </program>
+
+
+ <program>
+ <match>star.*gate</match>
+ </program>
+
+ <program>
+ <match>battlestar</match>
</program>
<program>
- <name>star trek</name>
+ <match>star trek</match>
</program>
<program>
- <name>shouf shouf</name>
+ <match>shouf shouf</match>
</program>
<program>
- <name>red dwarf</name>
+ <match>red dwarf</match>
</program>
<program>
- <name>top gear</name>
+ <match>top gear</match>
</program>
<program>
- <name>brainiac</name>
+ <match>brainiac</match>
</program>
<program>
- <name>lois.*clark</name>
+ <match>lois.*clark</match>
</program>
</programs>
import java.util.EnumMap;
import java.util.List;
import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.wamblee.conditions.Condition;
import org.wamblee.crawler.Action;
import org.wamblee.crawler.Configuration;
import org.wamblee.crawler.Crawler;
InputStream programConfigFile = new FileInputStream(new File(
aProgramConfig));
- Condition<Program> programCondition = new ProgramConfigurationParser()
+ List<ProgramFilter> programFilters = new ProgramConfigurationParser()
.parse(programConfigFile);
- recordInterestingShows(programCondition, guide);
+ recordInterestingShows(programFilters, guide);
} finally {
os.flush();
os.close();
* @throws MessagingException
* In case of problems sending a summary mail.
*/
- private void recordInterestingShows(Condition<Program> aProgramCondition,
+ private void recordInterestingShows(List<ProgramFilter> aProgramCondition,
TVGuide aGuide) throws MessagingException {
- MatchVisitor matcher = new MatchVisitor(aProgramCondition);
- aGuide.accept(matcher);
- List<Program> programs = matcher.getMatches();
- EnumMap<RecordingResult, List<Program>> messages = new EnumMap<RecordingResult, List<Program>>(
- RecordingResult.class);
- for (RecordingResult result : RecordingResult.values()) {
- messages.put(result, new ArrayList<Program>());
- }
- for (Program program : programs) {
- Program.RecordingResult result = program.record();
- messages.get(result).add(program);
+
+ Set<Program> showsToRecord = new TreeSet<Program>(new Program.TimeSorter());
+ Set<Program> interestingShows = new TreeSet<Program>(new Program.TimeSorter());
+
+ for (ProgramFilter filter : aProgramCondition) {
+ List<Program> programs = filter.apply(aGuide);
+ switch (filter.getAction()) {
+ case RECORD: {
+ for (Program program: programs) {
+ showsToRecord.add(program);
+ }
+ break;
+ }
+ case NOTIFY: {
+ for (Program program: programs) {
+ if ( program.isRecordingPossible()) {
+ interestingShows.add(program);
+ }
+ }
+ break;
+ }
+ default: {
+ throw new RuntimeException("Unknown action '" + filter.getAction() + "'");
+ }
+ }
}
+
+ EnumMap<RecordingResult, List<Program>> messages = recordShows(showsToRecord);
+
String msg = "Summary of KiSS crawler: \n\n\n";
for (RecordingResult result : RecordingResult.values()) {
if (messages.get(result).size() > 0) {
msg += result.getDescription() + "\n\n";
for (Program program : messages.get(result)) {
- msg += program + "\n";
+ msg += program + "\n\n";
}
}
}
- if (programs.size() == 0) {
+
+ if ( interestingShows.size() > 0 ) {
+ msg += "Possibly interesting shows:\n\n";
+ for (Program program: interestingShows) {
+ msg += program + "\n\n";
+ }
+ }
+ if (showsToRecord.size() + interestingShows.size() == 0) {
msg += "No suitable programs found";
}
sendMail(msg);
}
+ /**
+ * Records shows.
+ * @param showsToRecord Shows to record.
+ * @return Recording results.
+ */
+ private EnumMap<RecordingResult, List<Program>> recordShows(Set<Program> showsToRecord) {
+ EnumMap<RecordingResult, List<Program>> messages = new EnumMap<RecordingResult, List<Program>>(
+ RecordingResult.class);
+ for (RecordingResult result : RecordingResult.values()) {
+ messages.put(result, new ArrayList<Program>());
+ }
+
+ for (Program program : showsToRecord) {
+ Program.RecordingResult result = program.record();
+ messages.get(result).add(program);
+ }
+ return messages;
+ }
+
/**
* Creates the crawler.
*
package org.wamblee.crawler.kiss;
+import java.util.Comparator;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wamblee.crawler.Action;
*/
public class Program {
+ public static class TimeSorter implements Comparator<Program> {
+
+ /* (non-Javadoc)
+ * @see java.util.Comparator#compare(T, T)
+ */
+ public int compare(Program o1, Program o2) {
+ return o1.getInterval().getBegin().compareTo(o2.getInterval().getBegin());
+ }
+ }
+
private static final Log LOG = LogFactory.getLog(Program.class);
/**
public TimeInterval getInterval() {
return _interval;
}
+
+ /**
+ * Checks if recording is possible.
+ * @return True iff recording is possible.
+ */
+ public boolean isRecordingPossible() {
+ try {
+ Action record = _programInfo.execute().getAction(RECORD_ACTION);
+ if (record == null) {
+ return false;
+ }
+ return true;
+ } catch (PageException e) {
+ return false;
+ }
+ }
/**
* Records the show.
+ ")" + "\n"
+ (INDENT + _description).replaceAll("\n", "\n" + INDENT);
}
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if ( !(obj instanceof Program)) {
+ return false;
+ }
+ Program program = (Program)obj;
+ return getName().equals(program.getName()) &&
+ _programInfo.equals(program._programInfo);
+ }
}
import java.util.Iterator;
import java.util.List;
+import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
+import org.wamblee.conditions.AndCondition;
import org.wamblee.conditions.Condition;
-import org.wamblee.conditions.OrCondition;
+import org.wamblee.conditions.PropertyRegexCondition;
+import org.wamblee.crawler.kiss.ProgramFilter.ProgramAction;
/**
* Parse the configuration of desired programs.
*/
-public class ProgramConfigurationParser {
+class ProgramConfigurationParser {
private static final String ELEM_PROGRAM = "program";
- private static final String ELEM_PATTERN = "name";
+ private static final String ELEM_PATTERN = "match";
+
+ private static final String ELEM_ACTION = "action";
+
+ private static final String ACTION_NOTIFY = "notify";
/**
* Parses the condition used to match the desired programs.
* Input stream to parse from.
* @return Condition.
*/
- Condition<Program> parse(InputStream aStream) {
+ List<ProgramFilter> parse(InputStream aStream) {
+ List<ProgramFilter> filters = new ArrayList<ProgramFilter>();
try {
SAXReader reader = new SAXReader();
Document document = reader.read(aStream);
Element root = document.getRootElement();
- List<Condition<Program>> conditions = new ArrayList<Condition<Program>>();
for (Iterator i = root.elementIterator(ELEM_PROGRAM); i.hasNext();) {
Element program = (Element) i.next();
- String pattern = ".*" + program.element(ELEM_PATTERN).getText()
- + ".*";
- conditions.add(new ProgramNameMatcher(pattern));
+
+ Element actionElem = program.element(ELEM_ACTION);
+ ProgramAction action = ProgramAction.RECORD;
+ if (actionElem != null) {
+ if (actionElem.getText().equals(ACTION_NOTIFY)) {
+ action = ProgramAction.NOTIFY;
+ }
+ }
+ List<Condition<Program>> regexConditions =
+ new ArrayList<Condition<Program>>();
+ for (Iterator j = program.elementIterator(ELEM_PATTERN); j.hasNext(); ) {
+ Element patternElem = (Element)j.next();
+ String fieldName = "name";
+ Attribute fieldAttribute = patternElem.attribute("field");
+ if ( fieldAttribute != null ) {
+ fieldName = fieldAttribute.getText();
+ }
+ String pattern = ".*" + patternElem.getText()
+ + ".*";
+ regexConditions.add(new PropertyRegexCondition<Program>(fieldName, pattern, true));
+ }
+ Condition<Program> condition = new AndCondition<Program>(regexConditions);
+ filters.add(new ProgramFilter(condition, action));
}
- return new OrCondition<Program>(conditions);
+ return filters;
} catch (DocumentException e) {
throw new RuntimeException("Error parsing program configuraiton", e);
}
--- /dev/null
+/*
+ * Copyright 2005 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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;
+
+import java.util.List;
+
+import org.wamblee.conditions.Condition;
+
+
+/**
+ * Obtains a list of interesting programs from a TV guide and decides
+ * what to do with them.
+ */
+public class ProgramFilter {
+
+ public enum ProgramAction { RECORD, NOTIFY };
+
+ private Condition<Program> _condition;
+
+ private ProgramAction _action;
+
+ public ProgramFilter(Condition<Program> aCondition, ProgramAction aAction) {
+ _condition = aCondition;
+ _action = aAction;
+ }
+
+ public ProgramAction getAction() {
+ return _action;
+ }
+
+ public List<Program> apply(TVGuide aGuide) {
+ MatchVisitor matcher = new MatchVisitor(_condition);
+ aGuide.accept(matcher);
+ return matcher.getMatches();
+ }
+}
import java.text.NumberFormat;
/**
- * TIme at which a program starts or ends.
+ * TIme at which a program starts or ends.
*/
-public class Time {
+public class Time implements Comparable {
/**
- * Number of seconds per minute.
+ * Number of seconds per minute.
*/
private static final double SECONDS_PER_MINUTE = 60.0;
/**
- * Hour of the time.
+ * Hour of the time.
*/
private int _hour;
/**
- * Minute of the hour.
+ * Minute of the hour.
*/
private int _minute;
/**
- * Constructs the time.
- * @param aHour Hour.
- * @param aMinute Minute.
+ * Constructs the time.
+ *
+ * @param aHour
+ * Hour.
+ * @param aMinute
+ * Minute.
*/
public Time(int aHour, int aMinute) {
_hour = aHour;
}
/**
- * Gets the hour.
- * @return Hour.
+ * Gets the hour.
+ *
+ * @return Hour.
*/
public int getHour() {
return _hour;
/**
* Gets te minute.
- * @return Minute.
+ *
+ * @return Minute.
*/
public int getMinute() {
return _minute;
}
/**
- * Convert time to floating point value. Useful for comparing two times.
- * @return Converted value.
+ * Convert time to floating point value. Useful for comparing two times.
+ *
+ * @return Converted value.
*/
float asFloat() {
return (float) _hour + (float) _minute / (float) SECONDS_PER_MINUTE;
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if ( !(obj instanceof Time )) {
+ return false;
+ }
+ return toString().equals(obj.toString());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(T)
+ */
+ public int compareTo(Object o) {
+ if ( !(o instanceof Time)) {
+ throw new RuntimeException("object not an instance of Time");
+ }
+ Time time = (Time)o;
+ return new Float(asFloat()).compareTo(new Float(time.asFloat()));
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
}
boolean isUncertain() {
return _begin.asFloat() > _end.asFloat();
}
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)j
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if ( !(obj instanceof TimeInterval)) {
+ return false;
+ }
+ return obj.toString().equals(obj.toString());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return _begin.hashCode();
+ }
}
*/
private Pattern _regex;
+ /**
+ * Whether or not to convert the value to lowercase before matching.
+ */
+ private boolean _tolower;
+
/**
* Constructs the condition.
* @param aProperty Name of the property to examine.
* @param aRegex Regular expression to use.
+ * @param aTolower Whether or not to convert the value to lowercase before matching.
*/
- public PropertyRegexCondition(String aProperty, String aRegex) {
+ public PropertyRegexCondition(String aProperty, String aRegex, boolean aTolower) {
_property = aProperty;
_regex = Pattern.compile(aRegex);
+ _tolower = aTolower;
}
/* (non-Javadoc)
public boolean matches(T aObject) {
try {
String value = PropertyUtils.getProperty(aObject, _property) + "";
+ if ( _tolower ) {
+ value = value.toLowerCase();
+ }
Matcher matcher = _regex.matcher(value);
return matcher.matches();
} catch (IllegalAccessException e) {