2014-04-19 02:30:29 -04:00
package net.filebot.similarity ;
2009-01-24 19:08:57 -05:00
2016-10-07 09:19:49 -04:00
import static java.util.Arrays.* ;
2013-09-11 13:22:00 -04:00
import static java.util.Collections.* ;
import static java.util.regex.Pattern.* ;
2016-10-07 09:19:49 -04:00
import static java.util.stream.Collectors.* ;
2014-04-19 02:30:29 -04:00
import static net.filebot.util.FileUtilities.* ;
2016-04-02 05:07:10 -04:00
import static net.filebot.util.RegularExpressions.* ;
2016-01-09 23:54:35 -05:00
import static net.filebot.util.StringUtilities.* ;
2012-02-10 12:14:38 -05:00
2013-04-18 06:03:41 -04:00
import java.io.File ;
2009-01-24 19:08:57 -05:00
import java.util.ArrayList ;
2016-10-07 09:19:49 -04:00
import java.util.IntSummaryStatistics ;
2013-09-18 01:02:55 -04:00
import java.util.LinkedHashSet ;
2009-01-24 19:08:57 -05:00
import java.util.List ;
2016-10-30 17:03:39 -04:00
import java.util.Objects ;
2013-09-18 01:02:55 -04:00
import java.util.Set ;
2016-10-07 09:19:49 -04:00
import java.util.function.Function ;
2009-07-23 10:25:43 -04:00
import java.util.regex.MatchResult ;
2009-01-24 19:08:57 -05:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
2016-10-07 09:19:49 -04:00
import java.util.stream.IntStream ;
2009-01-24 19:08:57 -05:00
public class SeasonEpisodeMatcher {
2013-09-18 01:02:55 -04:00
2015-08-26 18:08:48 -04:00
public static final SeasonEpisodeFilter LENIENT_SANITY = new SeasonEpisodeFilter ( 99 , 999 , 9999 , 1970 , 2100 ) ;
2013-11-02 13:24:11 -04:00
public static final SeasonEpisodeFilter DEFAULT_SANITY = new SeasonEpisodeFilter ( 50 , 50 , 1000 , 1970 , 2100 ) ;
2013-11-27 12:09:19 -05:00
public static final SeasonEpisodeFilter STRICT_SANITY = new SeasonEpisodeFilter ( 10 , 30 , - 1 , - 1 , - 1 ) ;
2013-09-18 01:02:55 -04:00
2013-11-03 08:08:50 -05:00
private SeasonEpisodeParser [ ] patterns ;
2013-04-18 06:03:41 -04:00
private Pattern seasonPattern ;
2013-09-18 01:02:55 -04:00
2011-12-28 19:41:27 -05:00
public SeasonEpisodeMatcher ( SeasonEpisodeFilter sanity , boolean strict ) {
2013-11-03 08:08:50 -05:00
// define variables
2016-12-30 23:35:40 -05:00
SeasonEpisodePattern Season_00_Episode_00 , S00E00SEQ , S00E00 , SxE1_SxE2 , SxE , Dot101 , E01E02SEQ , EP0 , Num101_TOKEN , E1of2 , Num101_SUBSTRING ;
2013-09-18 01:02:55 -04:00
2012-03-04 19:49:11 -05:00
// match patterns like Season 01 Episode 02, ...
2016-10-07 09:19:49 -04:00
Season_00_Episode_00 = new SeasonEpisodePattern ( null , " (?<! \\ p{Alnum})(?i:season|series)[^ \\ p{Alnum}]{0,3}( \\ d{1,4})[^ \\ p{Alnum}]{0,3}(?i:episode)[^ \\ p{Alnum}]{0,3}(( \\ d{1,3}( \\ D|$))+)[^ \\ p{Alnum}]{0,3}(?! \\ p{Digit}) " , m - > {
return range ( m . group ( 1 ) , m . group ( 2 ) ) ;
} ) ;
2013-09-18 01:02:55 -04:00
2015-09-09 09:41:48 -04:00
// match patterns like S01E01-E05
2016-10-07 09:19:49 -04:00
S00E00SEQ = new SeasonEpisodePattern ( null , " (?<! \\ p{Alnum}|[-])[Ss]( \\ d{1,2}| \\ d{4})[Ee]( \\ d{2,3})[-][Ee]( \\ d{2,3})(?! \\ p{Alnum}|[-]) " , m - > {
return range ( m . group ( 1 ) , m . group ( 2 ) , m . group ( 3 ) ) ;
} ) ;
2015-09-09 09:41:48 -04:00
2012-03-17 15:02:04 -04:00
// match patterns like S01E01, s01e02, ... [s01]_[e02], s01.e02, s01e02a, s2010e01 ... s01e01-02-03-04, [s01]_[e01-02-03-04] ...
2018-08-25 01:50:06 -04:00
S00E00 = new SeasonEpisodePattern ( null , " (?<! \\ p{Digit})[Ss]( \\ d{1,2}| \\ d{4})[^ \\ p{Alnum}]{0,3}(?i:ep|e|p|-)(((?<=[^._ ])[Ee]?[Pp]? \\ d{1,3}( \\ D|$))+) " , m - > {
2016-10-07 09:19:49 -04:00
return multi ( m . group ( 1 ) , m . group ( 2 ) ) ;
} ) ;
2013-09-18 01:02:55 -04:00
2014-03-22 03:46:40 -04:00
// match patterns 1x01-1x02, ...
2016-10-07 09:19:49 -04:00
SxE1_SxE2 = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum})( \\ d{1,2}x \\ d{2}([-._ ] \\ d{1,2}x \\ d{2})+)(?! \\ p{Digit}) " , m - > {
return pairs ( m . group ( ) ) ;
} ) ;
2014-03-22 03:46:40 -04:00
2012-03-17 15:02:04 -04:00
// match patterns like 1x01, 1.02, ..., 1x01a, 10x01, 10.02, ... 1x01-02-03-04, 1x01x02x03x04 ...
2016-10-07 09:19:49 -04:00
SxE = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum})( \\ d{1,2})[xe](((?<=[^._ ]) \\ d{2,3}( \\ D|$))+) " , m - > {
return multi ( m . group ( 1 ) , m . group ( 2 ) ) ;
} ) ;
2013-10-20 09:06:58 -04:00
// match patterns 1.02, ..., 10.02, ...
2016-10-07 09:19:49 -04:00
Dot101 = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum}| \\ d{4}[.])( \\ d{1,2})[.](((?<=[^._ ]) \\ d{2}( \\ D|$))+) " , m - > {
return multi ( m . group ( 1 ) , m . group ( 2 ) ) ;
} ) ;
2013-09-18 01:02:55 -04:00
2016-12-30 23:35:40 -05:00
// match patterns like 101-105
E01E02SEQ = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum}|[-])( \\ d{2,3})[-]( \\ d{2,3})(?! \\ p{Alnum}|[-]) " , m - > {
return range ( null , m . group ( 1 ) , m . group ( 2 ) ) ;
} ) ;
2012-02-10 12:14:38 -05:00
// match patterns like ep1, ep.1, ...
2016-11-21 18:01:51 -05:00
EP0 = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum})( \\ d{2}| \\ d{4})?[ \\ P{Alnum}]{0,3}(((?i:e|ep|episode|p|part)[ \\ P{Alnum}]{0,3} \\ d{1,3})+)(?! \\ p{Digit}) " , m - > {
return multi ( m . group ( 1 ) , m . group ( 2 ) ) ;
2016-10-07 09:19:49 -04:00
} ) ;
2013-09-18 01:02:55 -04:00
// match patterns like 01, 102, 1003, 10102 (enclosed in separators)
2016-10-07 09:19:49 -04:00
Num101_TOKEN = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum})([0-2]? \\ d?)( \\ d{2})( \\ d{2})?(?! \\ p{Alnum}) " , m - > {
2016-10-07 16:49:08 -04:00
return numbers ( m . group ( 1 ) , streamCapturingGroups ( m ) . skip ( 1 ) . toArray ( String [ ] : : new ) ) ;
2016-10-07 09:19:49 -04:00
} ) ;
2013-09-18 01:02:55 -04:00
2016-10-07 09:19:49 -04:00
// match patterns like "1 of 2" as Episode 1
E1of2 = new SeasonEpisodePattern ( sanity , " (?<! \\ p{Alnum})( \\ d{1,2})[^._ ]?(?i:of)[^._ ]?( \\ d{1,2})(?! \\ p{Digit}) " , m - > {
return single ( null , m . group ( 1 ) ) ;
} ) ;
2014-03-08 03:25:04 -05:00
2013-11-27 13:49:15 -05:00
// (last-resort) match patterns like 101, 102 (and greedily just grab the first)
2016-10-07 09:19:49 -04:00
Num101_SUBSTRING = new SeasonEpisodePattern ( STRICT_SANITY , " (?<! \\ p{Digit})( \\ d{1})( \\ d{2})(?! \\ p{Digit})(.*) " , m - > {
return single ( m . group ( 1 ) , m . group ( 2 ) ) ;
} ) ;
2013-11-27 12:09:19 -05:00
2011-12-28 19:41:27 -05:00
// only use S00E00 and SxE pattern in strict mode
if ( strict ) {
2015-09-09 09:41:48 -04:00
patterns = new SeasonEpisodeParser [ ] { Season_00_Episode_00 , S00E00SEQ , S00E00 , SxE1_SxE2 , SxE , Dot101 } ;
2013-11-03 08:08:50 -05:00
} else {
2016-12-30 23:35:40 -05:00
patterns = new SeasonEpisodeParser [ ] { Season_00_Episode_00 , S00E00SEQ , S00E00 , SxE1_SxE2 , SxE , Dot101 , E01E02SEQ , new SeasonEpisodeUnion ( EP0 , Num101_TOKEN , E1of2 ) , Num101_SUBSTRING } ;
2011-12-28 19:41:27 -05:00
}
2013-09-18 01:02:55 -04:00
2013-04-18 06:03:41 -04:00
// season folder pattern for complementing partial sxe info from filename
2014-04-27 23:57:35 -04:00
seasonPattern = compile ( " Season[-._ ]?( \\ d{1,2}) " , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2009-01-24 19:08:57 -05:00
}
2013-09-18 01:02:55 -04:00
2016-10-07 09:19:49 -04:00
protected List < SxE > single ( String season , String episode ) {
return singletonList ( new SxE ( season , episode ) ) ;
}
protected List < SxE > multi ( String season , String . . . episodes ) {
2016-11-21 18:01:51 -05:00
Integer s = matchInteger ( season ) ;
2016-10-07 09:19:49 -04:00
return stream ( episodes ) . flatMap ( e - > matchIntegers ( e ) . stream ( ) ) . map ( e - > new SxE ( s , e ) ) . collect ( toList ( ) ) ;
}
protected List < SxE > range ( String season , String . . . episodes ) {
IntSummaryStatistics stats = stream ( episodes ) . flatMap ( s - > matchIntegers ( s ) . stream ( ) ) . mapToInt ( i - > i ) . summaryStatistics ( ) ;
2017-03-30 15:25:43 -04:00
// range patterns without season are more prone to false positives, so we need to do some extra sanity checks (e.g. Episode 01-50 is probably not a multi-episode but some sort of season pack)
if ( season = = null & & stats . getMax ( ) - stats . getMin ( ) > = 9 ) {
return emptyList ( ) ;
}
2016-12-30 23:35:40 -05:00
Integer s = matchInteger ( season ) ;
return IntStream . rangeClosed ( stats . getMin ( ) , stats . getMax ( ) ) . boxed ( ) . map ( e - > new SxE ( s , e ) ) . collect ( toList ( ) ) ;
2016-10-07 09:19:49 -04:00
}
protected List < SxE > pairs ( String text ) {
List < SxE > matches = new ArrayList < SxE > ( 2 ) ;
// SxE-SxE-SxE
String [ ] numbers = NON_DIGIT . split ( text ) ;
for ( int i = 0 ; i < numbers . length ; i + = 2 ) {
matches . add ( new SxE ( numbers [ i ] , numbers [ i + 1 ] ) ) ;
}
return matches ;
}
protected List < SxE > numbers ( String head , String . . . tail ) {
List < SxE > matches = new ArrayList < SxE > ( 2 ) ;
// interpret match as season and episode, but ignore 001 => 0x01 Season 0 matches
for ( String t : tail ) {
SxE sxe = new SxE ( head , t ) ;
if ( sxe . season > 0 ) {
matches . add ( sxe ) ;
}
}
// interpret match both ways, as SxE match as well as episode number only match if it's not an double episode
if ( tail . length = = 1 ) {
SxE absolute = new SxE ( null , head + tail [ 0 ] ) ;
if ( ! matches . contains ( absolute ) ) {
matches . add ( absolute ) ;
}
}
// return both matches, unless they are one and the same
return matches ;
}
2009-01-24 19:08:57 -05:00
/ * *
* Try to get season and episode numbers for the given string .
2015-07-25 18:47:19 -04:00
*
2013-09-18 01:02:55 -04:00
* @param name
* match this string against the a set of know patterns
* @return the matches returned by the first pattern that returns any matches for this string , or null if no pattern returned any matches
2009-01-24 19:08:57 -05:00
* /
public List < SxE > match ( CharSequence name ) {
2013-11-03 08:08:50 -05:00
for ( SeasonEpisodeParser pattern : patterns ) {
2009-01-24 19:08:57 -05:00
List < SxE > match = pattern . match ( name ) ;
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
if ( ! match . isEmpty ( ) ) {
// current pattern did match
return match ;
}
}
2013-04-18 06:03:41 -04:00
return null ;
}
2013-09-18 01:02:55 -04:00
2013-04-18 06:03:41 -04:00
public List < SxE > match ( File file ) {
2013-09-21 01:10:27 -04:00
// take folder name into consideration as much as file name but put priority on file name
2013-11-27 13:49:15 -05:00
List < String > tail = tokenizeTail ( file ) ;
2013-09-18 01:02:55 -04:00
2013-11-03 08:08:50 -05:00
for ( SeasonEpisodeParser pattern : patterns ) {
2013-11-27 13:49:15 -05:00
for ( int t = 0 ; t < tail . size ( ) ; t + + ) {
List < SxE > match = pattern . match ( tail . get ( t ) ) ;
2013-09-21 01:10:27 -04:00
if ( ! match . isEmpty ( ) ) {
// current pattern did match
for ( int i = 0 ; i < match . size ( ) ; i + + ) {
2013-11-27 13:49:15 -05:00
if ( match . get ( i ) . season < 0 & & t < tail . size ( ) - 1 ) {
Matcher sm = seasonPattern . matcher ( tail . get ( t + 1 ) ) ;
2013-09-21 01:10:27 -04:00
if ( sm . find ( ) ) {
match . set ( i , new SxE ( Integer . parseInt ( sm . group ( 1 ) ) , match . get ( i ) . episode ) ) ;
}
2013-04-18 06:03:41 -04:00
}
}
2013-09-21 01:10:27 -04:00
return match ;
2013-04-18 06:03:41 -04:00
}
}
}
2009-01-24 19:08:57 -05:00
return null ;
}
2013-09-18 01:02:55 -04:00
2013-11-27 13:49:15 -05:00
protected List < String > tokenizeTail ( File file ) {
List < String > tail = new ArrayList < String > ( 2 ) ;
for ( File f : listPathTail ( file , 2 , true ) ) {
tail . add ( getName ( f ) ) ;
}
return tail ;
}
2009-07-23 10:25:43 -04:00
public int find ( CharSequence name , int fromIndex ) {
2013-11-03 08:08:50 -05:00
for ( SeasonEpisodeParser pattern : patterns ) {
2009-07-23 10:25:43 -04:00
int index = pattern . find ( name , fromIndex ) ;
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
if ( index > = 0 ) {
// current pattern did match
return index ;
}
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
return - 1 ;
}
2013-09-18 01:02:55 -04:00
2014-08-27 02:33:27 -04:00
public String head ( String name ) {
int seasonEpisodePosition = find ( name , 0 ) ;
if ( seasonEpisodePosition > 0 ) {
return name . substring ( 0 , seasonEpisodePosition ) . trim ( ) ;
}
return null ;
}
2016-12-13 22:01:04 -05:00
public static class SxE implements Comparable < SxE > {
2013-09-18 01:02:55 -04:00
2009-05-03 11:21:04 -04:00
public static final int UNDEFINED = - 1 ;
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
public final int season ;
public final int episode ;
2013-09-18 01:02:55 -04:00
2010-10-24 12:33:38 -04:00
public SxE ( Integer season , Integer episode ) {
this . season = season ! = null ? season : UNDEFINED ;
this . episode = episode ! = null ? episode : UNDEFINED ;
2009-01-24 19:08:57 -05:00
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
public SxE ( String season , String episode ) {
this . season = parse ( season ) ;
this . episode = parse ( episode ) ;
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
protected int parse ( String number ) {
2009-05-03 11:21:04 -04:00
try {
return Integer . parseInt ( number ) ;
} catch ( Exception e ) {
return UNDEFINED ;
}
2009-01-24 19:08:57 -05:00
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
@Override
public boolean equals ( Object object ) {
if ( object instanceof SxE ) {
SxE other = ( SxE ) object ;
return this . season = = other . season & & this . episode = = other . episode ;
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
return false ;
}
2013-09-18 01:02:55 -04:00
2016-12-13 22:01:04 -05:00
@Override
public int compareTo ( SxE other ) {
return season < other . season ? - 1 : season ! = other . season ? 1 : episode < other . episode ? - 1 : episode ! = other . episode ? 1 : 0 ;
}
2009-06-13 05:53:48 -04:00
@Override
public int hashCode ( ) {
2016-10-30 17:03:39 -04:00
return Objects . hash ( season , episode ) ;
2009-06-13 05:53:48 -04:00
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
@Override
public String toString ( ) {
2009-08-06 07:51:30 -04:00
return season > = 0 ? String . format ( " %dx%02d " , season , episode ) : String . format ( " %02d " , episode ) ;
2009-01-24 19:08:57 -05:00
}
}
2013-09-18 01:02:55 -04:00
2011-11-22 09:44:54 -05:00
public static class SeasonEpisodeFilter {
2013-09-18 01:02:55 -04:00
2011-11-22 09:44:54 -05:00
public final int seasonLimit ;
public final int seasonEpisodeLimit ;
public final int absoluteEpisodeLimit ;
2013-11-02 13:24:11 -04:00
public final int seasonYearBegin ;
public final int seasonYearEnd ;
2013-09-18 01:02:55 -04:00
2013-11-02 13:24:11 -04:00
public SeasonEpisodeFilter ( int seasonLimit , int seasonEpisodeLimit , int absoluteEpisodeLimit , int seasonYearBegin , int seasonYearEnd ) {
2011-11-22 09:44:54 -05:00
this . seasonLimit = seasonLimit ;
this . seasonEpisodeLimit = seasonEpisodeLimit ;
this . absoluteEpisodeLimit = absoluteEpisodeLimit ;
2013-11-02 13:24:11 -04:00
this . seasonYearBegin = seasonYearBegin ;
this . seasonYearEnd = seasonYearEnd ;
2011-11-22 09:44:54 -05:00
}
2013-09-18 01:02:55 -04:00
2011-11-22 09:44:54 -05:00
boolean filter ( SxE sxe ) {
2013-11-02 13:24:11 -04:00
return ( sxe . season > = 0 & & ( sxe . season < seasonLimit | | ( sxe . season > seasonYearBegin & & sxe . season < seasonYearEnd ) ) & & sxe . episode < seasonEpisodeLimit ) | | ( sxe . season < 0 & & sxe . episode < absoluteEpisodeLimit ) ;
2011-11-22 09:44:54 -05:00
}
2016-12-30 23:35:40 -05:00
public boolean filter ( SxE value , List < SxE > sequence ) {
return filter ( value ) & & sequence . stream ( ) . filter ( other - > {
return ( value . season = = SxE . UNDEFINED ) = = ( other . season = = SxE . UNDEFINED ) & & other . compareTo ( value ) > 0 ;
} ) . count ( ) = = 0 ;
}
2011-11-22 09:44:54 -05:00
}
2013-09-18 01:02:55 -04:00
2013-11-03 08:08:50 -05:00
public static interface SeasonEpisodeParser {
public abstract List < SxE > match ( CharSequence name ) ;
public abstract int find ( CharSequence name , int fromIndex ) ;
}
public static class SeasonEpisodePattern implements SeasonEpisodeParser {
2013-09-18 01:02:55 -04:00
2016-10-07 09:19:49 -04:00
protected Pattern pattern ;
protected Function < MatchResult , List < SxE > > process ;
protected SeasonEpisodeFilter sanity ;
2013-09-18 01:02:55 -04:00
2011-11-22 09:44:54 -05:00
public SeasonEpisodePattern ( SeasonEpisodeFilter sanity , String pattern ) {
2016-10-07 09:19:49 -04:00
this ( sanity , pattern , m - > singletonList ( new SxE ( m . group ( 1 ) , m . group ( 2 ) ) ) ) ;
}
public SeasonEpisodePattern ( SeasonEpisodeFilter sanity , String pattern , Function < MatchResult , List < SxE > > process ) {
2009-07-23 10:25:43 -04:00
this . pattern = Pattern . compile ( pattern ) ;
2016-10-07 09:19:49 -04:00
this . process = process ;
2011-11-22 09:44:54 -05:00
this . sanity = sanity ;
2009-01-24 19:08:57 -05:00
}
2013-09-18 01:02:55 -04:00
2009-07-29 16:31:08 -04:00
public Matcher matcher ( CharSequence name ) {
return pattern . matcher ( name ) ;
}
2013-09-18 01:02:55 -04:00
2013-11-03 08:08:50 -05:00
@Override
2009-01-24 19:08:57 -05:00
public List < SxE > match ( CharSequence name ) {
2009-07-23 10:25:43 -04:00
// name will probably contain no more than two matches
List < SxE > matches = new ArrayList < SxE > ( 2 ) ;
2013-09-18 01:02:55 -04:00
2009-07-29 16:31:08 -04:00
Matcher matcher = matcher ( name ) ;
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
while ( matcher . find ( ) ) {
2016-10-07 09:19:49 -04:00
for ( SxE value : process . apply ( matcher ) ) {
2016-12-30 23:35:40 -05:00
if ( sanity = = null | | sanity . filter ( value , matches ) ) {
matches . add ( value ) ;
2011-11-22 09:44:54 -05:00
}
}
2009-01-24 19:08:57 -05:00
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
return matches ;
}
2013-09-18 01:02:55 -04:00
2013-11-03 08:08:50 -05:00
@Override
2009-07-23 10:25:43 -04:00
public int find ( CharSequence name , int fromIndex ) {
2011-11-22 09:44:54 -05:00
Matcher matcher = matcher ( name ) . region ( fromIndex , name . length ( ) ) ;
2013-09-18 01:02:55 -04:00
2011-11-22 09:44:54 -05:00
while ( matcher . find ( ) ) {
2016-10-07 09:19:49 -04:00
for ( SxE value : process . apply ( matcher ) ) {
2011-11-22 09:44:54 -05:00
if ( sanity = = null | | sanity . filter ( value ) ) {
return matcher . start ( ) ;
}
}
2009-07-29 16:31:08 -04:00
}
2013-09-18 01:02:55 -04:00
2009-01-24 19:08:57 -05:00
return - 1 ;
}
2014-04-26 13:31:03 -04:00
@Override
public String toString ( ) {
return pattern . pattern ( ) ;
}
2009-01-24 19:08:57 -05:00
}
2013-09-18 01:02:55 -04:00
2013-11-03 08:08:50 -05:00
public static class SeasonEpisodeUnion implements SeasonEpisodeParser {
private final SeasonEpisodeParser [ ] parsers ;
public SeasonEpisodeUnion ( SeasonEpisodeParser . . . parsers ) {
this . parsers = parsers ;
}
@Override
public List < SxE > match ( CharSequence name ) {
Set < SxE > matches = new LinkedHashSet < SxE > ( ) ;
for ( SeasonEpisodeParser it : parsers ) {
matches . addAll ( it . match ( name ) ) ;
}
return new ArrayList < SxE > ( matches ) ;
}
@Override
public int find ( CharSequence name , int fromIndex ) {
int min = - 1 ;
for ( SeasonEpisodeParser it : parsers ) {
int pos = it . find ( name , fromIndex ) ;
if ( pos > = 0 & & pos > min ) {
min = pos ;
}
}
return min ;
}
}
2009-01-24 19:08:57 -05:00
}