* added number-pattern similarity metric
* improved name-matching, normalizing of names * unit-test for new similarity metrics * improved PreferencesList * added EventList->List synchronizer * included GlazedLists in build
This commit is contained in:
parent
2b4218ffce
commit
d1775cf1b4
|
@ -60,15 +60,9 @@
|
|||
<include name="**/*.class" />
|
||||
</zipfileset>
|
||||
|
||||
<!--
|
||||
<zipfileset src="${lib.glazedlists}">
|
||||
<include name="ca/odell/glazedlists/*.class" />
|
||||
<include name="ca/odell/glazedlists/event/**" />
|
||||
<include name="ca/odell/glazedlists/matchers/**" />
|
||||
<include name="ca/odell/glazedlists/gui/**" />
|
||||
<include name="ca/odell/glazedlists/swing/**" />
|
||||
<include name="ca/odell/glazedlists/**" />
|
||||
</zipfileset>
|
||||
-->
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ca.odell.glazedlists.EventList;
|
||||
import ca.odell.glazedlists.event.ListEvent;
|
||||
import ca.odell.glazedlists.event.ListEventListener;
|
||||
|
||||
|
||||
//TODO: testcase, class doc
|
||||
public class ListChangeSynchronizer<E> implements ListEventListener<E> {
|
||||
|
||||
private final List<E> target;
|
||||
|
||||
|
||||
public ListChangeSynchronizer(EventList<E> source, List<E> target) {
|
||||
this.target = target;
|
||||
source.addListEventListener(this);
|
||||
}
|
||||
|
||||
|
||||
public void listChanged(ListEvent<E> listChanges) {
|
||||
EventList<E> source = listChanges.getSourceList();
|
||||
|
||||
// update target list
|
||||
while (listChanges.next()) {
|
||||
int index = listChanges.getIndex();
|
||||
int type = listChanges.getType();
|
||||
|
||||
switch (type) {
|
||||
case ListEvent.INSERT:
|
||||
target.add(index, source.get(index));
|
||||
break;
|
||||
case ListEvent.UPDATE:
|
||||
target.set(index, source.get(index));
|
||||
break;
|
||||
case ListEvent.DELETE:
|
||||
target.remove(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static <E> ListChangeSynchronizer<E> syncEventListToList(EventList<E> source, List<E> target) {
|
||||
return new ListChangeSynchronizer<E>(source, target);
|
||||
}
|
||||
|
||||
}
|
|
@ -34,6 +34,7 @@ import net.sourceforge.tuned.ProgressIterator;
|
|||
import net.sourceforge.tuned.ui.SelectButtonTextField;
|
||||
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
import ca.odell.glazedlists.BasicEventList;
|
||||
import ca.odell.glazedlists.EventList;
|
||||
import ca.odell.glazedlists.swing.AutoCompleteSupport;
|
||||
|
||||
|
@ -46,14 +47,13 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
|
|||
|
||||
private final SelectButtonTextField<S> searchField;
|
||||
|
||||
private final EventList<String> searchHistory;
|
||||
private final EventList<String> searchHistory = new BasicEventList<String>();
|
||||
|
||||
|
||||
public AbstractSearchPanel(String title, Icon icon, EventList<String> searchHistory) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public AbstractSearchPanel(String title, Icon icon) {
|
||||
super(title, icon);
|
||||
|
||||
this.searchHistory = searchHistory;
|
||||
|
||||
setLayout(new BorderLayout(10, 5));
|
||||
|
||||
searchField = new SelectButtonTextField<S>();
|
||||
|
@ -84,6 +84,15 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
|
|||
add(searchBox, BorderLayout.NORTH);
|
||||
add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
/*
|
||||
* TODO: fetchHistory
|
||||
// no need to care about thread-safety, history-lists are only accessed from the EDT
|
||||
CompositeList<Object> completionList = new CompositeList<Object>();
|
||||
|
||||
completionList.addMemberList((EventList) searchHistory);
|
||||
completionList.addMemberList(fetchHistory);
|
||||
*/
|
||||
|
||||
AutoCompleteSupport.install(searchField.getEditor(), searchHistory);
|
||||
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
|
@ -102,7 +111,7 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
|
|||
protected abstract URI getLink(S client, SearchResult searchResult);
|
||||
|
||||
|
||||
public List<String> getSearchHistory() {
|
||||
public EventList<String> getSearchHistory() {
|
||||
return searchHistory;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,20 +19,18 @@ import javax.swing.SwingWorker;
|
|||
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
import net.sourceforge.filebot.ui.panel.rename.match.Match;
|
||||
import net.sourceforge.filebot.ui.panel.rename.match.Matcher;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.LengthEqualsMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.MultiSimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.SimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.StringEqualsMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.StringSimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.matcher.Match;
|
||||
import net.sourceforge.filebot.ui.panel.rename.matcher.Matcher;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.CompositeSimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.NumericSimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.SimilarityMetric;
|
||||
import net.sourceforge.tuned.ui.ProgressDialog;
|
||||
import net.sourceforge.tuned.ui.SwingWorkerProgressMonitor;
|
||||
|
||||
|
||||
class MatchAction extends AbstractAction {
|
||||
|
||||
private MultiSimilarityMetric metrics;
|
||||
private CompositeSimilarityMetric metrics;
|
||||
|
||||
private final RenameList namesList;
|
||||
private final RenameList filesList;
|
||||
|
@ -50,7 +48,7 @@ class MatchAction extends AbstractAction {
|
|||
this.filesList = filesList;
|
||||
|
||||
// length similarity will only effect torrent <-> file matches
|
||||
metrics = new MultiSimilarityMetric(new StringSimilarityMetric(), new StringEqualsMetric(), new LengthEqualsMetric());
|
||||
metrics = new CompositeSimilarityMetric(new NumericSimilarityMetric());
|
||||
|
||||
setMatchName2File(true);
|
||||
}
|
||||
|
@ -69,7 +67,7 @@ class MatchAction extends AbstractAction {
|
|||
}
|
||||
|
||||
|
||||
public MultiSimilarityMetric getMetrics() {
|
||||
public CompositeSimilarityMetric getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
|
@ -92,7 +90,7 @@ class MatchAction extends AbstractAction {
|
|||
|
||||
ProgressDialog progressDialog = monitor.getProgressDialog();
|
||||
progressDialog.setTitle("Matching ...");
|
||||
progressDialog.setHeader("Matching ...");
|
||||
progressDialog.setHeader(progressDialog.getTitle());
|
||||
progressDialog.setIcon((Icon) getValue(SMALL_ICON));
|
||||
|
||||
backgroundMatcher.execute();
|
||||
|
|
|
@ -19,8 +19,8 @@ import javax.swing.event.ListSelectionEvent;
|
|||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.MultiSimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.similarity.SimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.CompositeSimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.SimilarityMetric;
|
||||
import net.sourceforge.tuned.ui.notification.SeparatorBorder;
|
||||
|
||||
|
||||
|
@ -70,7 +70,7 @@ class SimilarityPanel extends Box {
|
|||
}
|
||||
|
||||
|
||||
public void setMetrics(MultiSimilarityMetric metrics) {
|
||||
public void setMetrics(CompositeSimilarityMetric metrics) {
|
||||
grid.removeAll();
|
||||
updaterList.clear();
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
|
||||
|
||||
public abstract class AbstractNameSimilarityMetric implements SimilarityMetric {
|
||||
|
||||
@Override
|
||||
public float getSimilarity(ListEntry a, ListEntry b) {
|
||||
return getSimilarity(normalize(a.getName()), normalize(b.getName()));
|
||||
}
|
||||
|
||||
|
||||
protected String normalize(String name) {
|
||||
name = stripChecksum(name);
|
||||
name = normalizeSeparators(name);
|
||||
name = name.trim();
|
||||
name = name.toLowerCase();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
protected String normalizeSeparators(String name) {
|
||||
return name.replaceAll("[\\._ ]+", " ");
|
||||
}
|
||||
|
||||
|
||||
protected String stripChecksum(String name) {
|
||||
return name.replaceAll("\\[\\p{XDigit}{8}\\]", "");
|
||||
}
|
||||
|
||||
|
||||
public abstract float getSimilarity(String a, String b);
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
|
||||
|
||||
public class CompositeSimilarityMetric implements SimilarityMetric, Iterable<SimilarityMetric> {
|
||||
|
||||
private List<SimilarityMetric> similarityMetrics;
|
||||
|
||||
|
||||
public CompositeSimilarityMetric(SimilarityMetric... metrics) {
|
||||
similarityMetrics = Arrays.asList(metrics);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getSimilarity(ListEntry a, ListEntry b) {
|
||||
float similarity = 0;
|
||||
|
||||
for (SimilarityMetric metric : similarityMetrics) {
|
||||
similarity += metric.getSimilarity(a, b) / similarityMetrics.size();
|
||||
}
|
||||
|
||||
return similarity;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Average";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<SimilarityMetric> iterator() {
|
||||
return similarityMetrics.iterator();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.AbstractFileEntry;
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
|
||||
|
||||
public class LengthEqualsMetric implements SimilarityMetric {
|
||||
|
||||
@Override
|
||||
public float getSimilarity(ListEntry a, ListEntry b) {
|
||||
if ((a instanceof AbstractFileEntry) && (b instanceof AbstractFileEntry)) {
|
||||
long lengthA = ((AbstractFileEntry) a).getLength();
|
||||
long lengthB = ((AbstractFileEntry) b).getLength();
|
||||
|
||||
if (lengthA == lengthB)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Check whether file size is equal or not";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Length";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
|
||||
import uk.ac.shef.wit.simmetrics.similaritymetrics.AbstractStringMetric;
|
||||
import uk.ac.shef.wit.simmetrics.similaritymetrics.EuclideanDistance;
|
||||
import uk.ac.shef.wit.simmetrics.tokenisers.InterfaceTokeniser;
|
||||
import uk.ac.shef.wit.simmetrics.wordhandlers.DummyStopTermHandler;
|
||||
import uk.ac.shef.wit.simmetrics.wordhandlers.InterfaceTermHandler;
|
||||
|
||||
|
||||
public class NumericSimilarityMetric extends AbstractNameSimilarityMetric {
|
||||
|
||||
private final AbstractStringMetric metric;
|
||||
|
||||
|
||||
public NumericSimilarityMetric() {
|
||||
// I have absolutely no clue as to why, but I get a good matching behavior
|
||||
// when using my NumberTokensier with EuclideanDistance
|
||||
metric = new EuclideanDistance(new NumberTokeniser());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getSimilarity(String a, String b) {
|
||||
return metric.getSimilarity(a, b);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Similarity of number patterns";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Numbers";
|
||||
}
|
||||
|
||||
|
||||
private static class NumberTokeniser implements InterfaceTokeniser {
|
||||
|
||||
private final String delimiter = "(\\D)+";
|
||||
|
||||
|
||||
@Override
|
||||
public ArrayList<String> tokenizeToArrayList(String input) {
|
||||
ArrayList<String> tokens = new ArrayList<String>();
|
||||
|
||||
Scanner scanner = new Scanner(input);
|
||||
scanner.useDelimiter(delimiter);
|
||||
|
||||
while (scanner.hasNextInt()) {
|
||||
tokens.add(Integer.toString(scanner.nextInt()));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> tokenizeToSet(String input) {
|
||||
return new HashSet<String>(tokenizeToArrayList(input));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getShortDescriptionString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDelimiters() {
|
||||
return delimiter;
|
||||
}
|
||||
|
||||
private InterfaceTermHandler stopWordHandler = new DummyStopTermHandler();
|
||||
|
||||
|
||||
@Override
|
||||
public InterfaceTermHandler getStopWordHandler() {
|
||||
return stopWordHandler;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setStopWordHandler(InterfaceTermHandler stopWordHandler) {
|
||||
this.stopWordHandler = stopWordHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
|
||||
|
||||
public interface SimilarityMetric {
|
||||
|
||||
public float getSimilarity(ListEntry a, ListEntry b);
|
||||
|
||||
|
||||
public String getDescription();
|
||||
|
||||
|
||||
public String getName();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import uk.ac.shef.wit.simmetrics.similaritymetrics.AbstractStringMetric;
|
||||
import uk.ac.shef.wit.simmetrics.similaritymetrics.MongeElkan;
|
||||
import uk.ac.shef.wit.simmetrics.tokenisers.TokeniserQGram3Extended;
|
||||
|
||||
|
||||
public class StringSimilarityMetric extends AbstractNameSimilarityMetric {
|
||||
|
||||
private final AbstractStringMetric metric;
|
||||
|
||||
|
||||
public StringSimilarityMetric() {
|
||||
// I have absolutely no clue as to why, but I get a good matching behavior
|
||||
// when using MongeElkan with a QGram3Extended (far from perfect though)
|
||||
metric = new MongeElkan(new TokeniserQGram3Extended());
|
||||
|
||||
//TODO QGram3Extended VS Whitespace (-> normalized values)
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getSimilarity(String a, String b) {
|
||||
return metric.getSimilarity(a, b);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Similarity of names";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return metric.getShortDescriptionString();
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
|||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.filebot.ListChangeSynchronizer;
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.AbstractSearchPanel;
|
||||
|
@ -12,30 +13,28 @@ import net.sourceforge.filebot.ui.SelectDialog;
|
|||
import net.sourceforge.filebot.web.SearchResult;
|
||||
import net.sourceforge.filebot.web.SubtitleClient;
|
||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||
import net.sourceforge.tuned.BasicCachingList;
|
||||
import net.sourceforge.tuned.FunctionIterator;
|
||||
import net.sourceforge.tuned.ProgressIterator;
|
||||
import net.sourceforge.tuned.FunctionIterator.Function;
|
||||
import net.sourceforge.tuned.ui.SimpleIconProvider;
|
||||
import ca.odell.glazedlists.EventList;
|
||||
import ca.odell.glazedlists.GlazedLists;
|
||||
|
||||
|
||||
public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitlePackage, SubtitleDownloadPanel> {
|
||||
|
||||
public SubtitlePanel() {
|
||||
super("Subtitle", ResourceManager.getIcon("panel.subtitle"), globalSearchHistory());
|
||||
super("Subtitle", ResourceManager.getIcon("panel.subtitle"));
|
||||
|
||||
getHistoryPanel().setColumnHeader1("Show / Movie");
|
||||
getHistoryPanel().setColumnHeader2("Number of Subtitles");
|
||||
|
||||
getSearchField().getSelectButton().setModel(SubtitleClient.getAvailableSubtitleClients());
|
||||
getSearchField().getSelectButton().setIconProvider(SimpleIconProvider.forClass(SubtitleClient.class));
|
||||
}
|
||||
|
||||
|
||||
private static EventList<String> globalSearchHistory() {
|
||||
return GlazedLists.eventList(new BasicCachingList<String>(Settings.getSettings().asStringList(Settings.SUBTITLE_HISTORY)));
|
||||
|
||||
List<String> persistentSearchHistory = Settings.getSettings().asStringList(Settings.SUBTITLE_HISTORY);
|
||||
|
||||
getSearchHistory().addAll(persistentSearchHistory);
|
||||
|
||||
ListChangeSynchronizer.syncEventListToList(getSearchHistory(), persistentSearchHistory);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public class TVRageClient extends EpisodeListClient {
|
|||
List<SearchResult> searchResults = new ArrayList<SearchResult>(nodes.size());
|
||||
|
||||
for (Node node : nodes) {
|
||||
String showid = XPathUtil.selectString("showid", node);
|
||||
int showid = XPathUtil.selectInteger("showid", node);
|
||||
String name = XPathUtil.selectString("name", node);
|
||||
String link = XPathUtil.selectString("link", node);
|
||||
|
||||
|
@ -65,7 +65,7 @@ public class TVRageClient extends EpisodeListClient {
|
|||
@Override
|
||||
public ProgressIterator<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException, ParserConfigurationException {
|
||||
|
||||
String showId = ((TVRageSearchResult) searchResult).getShowId();
|
||||
int showId = ((TVRageSearchResult) searchResult).getShowId();
|
||||
String episodeListUri = String.format("http://" + host + "/feeds/episode_list.php?sid=" + showId);
|
||||
|
||||
Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(episodeListUri);
|
||||
|
@ -110,18 +110,18 @@ public class TVRageClient extends EpisodeListClient {
|
|||
|
||||
public static class TVRageSearchResult extends SearchResult {
|
||||
|
||||
private final String showId;
|
||||
private final int showId;
|
||||
private final String link;
|
||||
|
||||
|
||||
public TVRageSearchResult(String name, String showId, String link) {
|
||||
public TVRageSearchResult(String name, int showId, String link) {
|
||||
super(name);
|
||||
this.showId = showId;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
|
||||
public String getShowId() {
|
||||
public int getShowId() {
|
||||
return showId;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,28 +39,36 @@ public class PreferencesList<T> extends AbstractList<T> {
|
|||
|
||||
@Override
|
||||
public boolean add(T e) {
|
||||
prefs.put(key(size()), e);
|
||||
setImpl(size(), e);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//TODO: assert invalid index
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
copy(index, index + 1, size() - index);
|
||||
|
||||
setImpl(index, element);
|
||||
}
|
||||
|
||||
|
||||
private T setImpl(int index, T element) {
|
||||
return prefs.put(key(index), element);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return always null
|
||||
*/
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
|
||||
int lastIndex = size() - 1;
|
||||
|
||||
List<T> shiftList = new ArrayList<T>(subList(index, lastIndex + 1));
|
||||
|
||||
T value = shiftList.remove(0);
|
||||
|
||||
copy(index + 1, index, lastIndex - index);
|
||||
prefs.remove(key(lastIndex));
|
||||
|
||||
for (T element : shiftList) {
|
||||
set(index, element);
|
||||
index++;
|
||||
}
|
||||
|
||||
return value;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -69,7 +77,19 @@ public class PreferencesList<T> extends AbstractList<T> {
|
|||
if (index < 0 || index >= size())
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
||||
return prefs.put(key(index), element);
|
||||
return setImpl(index, element);
|
||||
}
|
||||
|
||||
|
||||
private void copy(int startIndex, int newStartIndex, int count) {
|
||||
if (count == 0 || startIndex == newStartIndex)
|
||||
return;
|
||||
|
||||
List<T> copy = new ArrayList<T>(subList(startIndex, startIndex + count));
|
||||
|
||||
for (int i = newStartIndex, n = 0; n < count; i++, n++) {
|
||||
setImpl(i, copy.get(n));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
import junit.framework.JUnit4TestAdapter;
|
||||
import junit.framework.Test;
|
||||
import net.sourceforge.filebot.FileBotTestSuite;
|
||||
import net.sourceforge.tuned.TunedTestSuite;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
@ -8,7 +9,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( { net.sourceforge.tuned.TestSuite.class, net.sourceforge.filebot.TestSuite.class })
|
||||
@SuiteClasses( { FileBotTestSuite.class, TunedTestSuite.class })
|
||||
public class AllTests {
|
||||
|
||||
public static Test suite() {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import junit.framework.JUnit4TestAdapter;
|
||||
import junit.framework.Test;
|
||||
import net.sourceforge.filebot.ui.panel.rename.MatcherTestSuite;
|
||||
import net.sourceforge.filebot.web.WebTestSuite;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( { MatcherTestSuite.class, WebTestSuite.class })
|
||||
public class FileBotTestSuite {
|
||||
|
||||
public static Test suite() {
|
||||
return new JUnit4TestAdapter(FileBotTestSuite.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import junit.framework.JUnit4TestAdapter;
|
||||
import junit.framework.Test;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.AbstractNameSimilarityMetricTest;
|
||||
import net.sourceforge.filebot.ui.panel.rename.metric.NumericSimilarityMetricTest;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( { AbstractNameSimilarityMetricTest.class, NumericSimilarityMetricTest.class })
|
||||
public class MatcherTestSuite {
|
||||
|
||||
public static Test suite() {
|
||||
return new JUnit4TestAdapter(MatcherTestSuite.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.sourceforge.tuned.TestUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class AbstractNameSimilarityMetricTest {
|
||||
|
||||
private static final BasicNameSimilarityMetric metric = new BasicNameSimilarityMetric();
|
||||
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> createParameters() {
|
||||
Map<String, String> matches = new LinkedHashMap<String, String>();
|
||||
|
||||
// normalize separators
|
||||
matches.put("test s01e01 first", "test.S01E01.First");
|
||||
matches.put("test s01e02 second", "test_S01E02_Second");
|
||||
matches.put("test s01e03 third", "__test__S01E03__Third__");
|
||||
matches.put("test s01e04 four", "test s01e04 four");
|
||||
|
||||
// strip checksum
|
||||
matches.put("test", "test [EF62DF13]");
|
||||
|
||||
// lower-case
|
||||
matches.put("the a-team", "The A-Team");
|
||||
|
||||
return TestUtil.asParameters(matches.entrySet().toArray());
|
||||
}
|
||||
|
||||
private Entry<String, String> entry;
|
||||
|
||||
|
||||
public AbstractNameSimilarityMetricTest(Entry<String, String> entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void normalize() {
|
||||
String normalizedName = entry.getKey();
|
||||
String unnormalizedName = entry.getValue();
|
||||
|
||||
assertEquals(normalizedName, metric.normalize(unnormalizedName));
|
||||
}
|
||||
|
||||
|
||||
private static class BasicNameSimilarityMetric extends AbstractNameSimilarityMetric {
|
||||
|
||||
@Override
|
||||
public float getSimilarity(String a, String b) {
|
||||
return a.equals(b) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Equals";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Equals";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename.metric;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.tuned.TestUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class NumericSimilarityMetricTest {
|
||||
|
||||
private static NumericSimilarityMetric metric = new NumericSimilarityMetric();
|
||||
|
||||
private static Map<String, String> matches = createMatches();
|
||||
|
||||
|
||||
public static Map<String, String> createMatches() {
|
||||
Map<String, String> matches = new LinkedHashMap<String, String>();
|
||||
|
||||
// lots of naming variations
|
||||
matches.put("Test - 1x01", "test.S01E01.Pilot.HDTV.XviD-FQM");
|
||||
matches.put("Test - 1x02", "test.S01E02.HDTV.XviD-DIMENSION");
|
||||
matches.put("Test - 1x03", "test.S01E03.Third.time.is.the.charm.DSR.XviD-2SD");
|
||||
matches.put("Test - 1x04", "test.S01E04.Four.Square.HDTV-FQM.eng");
|
||||
matches.put("Test - 1x05", "test.season.1.episode.05.DSR.eng");
|
||||
matches.put("Test - 1x06", "test.1x06.dsr.V1");
|
||||
matches.put("Test - 1x07", "test.s01e07.dsr.tempt.12.V0");
|
||||
matches.put("Test - 1x08", "test.s01e08.dsr.tempt.16.V0");
|
||||
matches.put("Test - 1x09", "Test - 1x09");
|
||||
matches.put("Test - 1x10", "test.s01e10.dsr.xvid-2sd.VO");
|
||||
matches.put("Test - 1x11", "Test - 01x11 - The Question");
|
||||
matches.put("Test - 1x12", "test.1x12.iht.VO");
|
||||
matches.put("Test - 1x13", "Test.S01E13.DSR.XviD-0TV");
|
||||
matches.put("Test - 1x14", "Test.S01E14.DSR.XviD-2SD");
|
||||
matches.put("Test - 1x15", "Test.S01E15.DSR.XviD-0TV");
|
||||
matches.put("Test - 1x16", "test.1x16.0tv.VO");
|
||||
matches.put("Test - 1x17", "Test.S01E17.42.is.the.answer.DSR.XviD-0TV");
|
||||
matches.put("Test - 1x18", "Test.S01E18.DSR.XviD-0TV");
|
||||
|
||||
// lots of numbers
|
||||
matches.put("The 4400 - 1x01", "the.4400.s1e01.pilot.720p");
|
||||
matches.put("The 4400 - 4x04", "the.4400.s4e04.eden.720p");
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> createParameters() {
|
||||
return TestUtil.asParameters(matches.keySet().toArray());
|
||||
}
|
||||
|
||||
private String normalizedName;
|
||||
|
||||
|
||||
public NumericSimilarityMetricTest(String normalizedName) {
|
||||
this.normalizedName = normalizedName;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getBestMatch() {
|
||||
String match = getBestMatch(normalizedName, matches.values());
|
||||
|
||||
assertEquals(matches.get(normalizedName), match);
|
||||
}
|
||||
|
||||
|
||||
public String getBestMatch(String value, Collection<String> testdata) {
|
||||
float maxSimilarity = -1;
|
||||
String mostSimilar = null;
|
||||
|
||||
for (String comparisonValue : testdata) {
|
||||
float similarity = metric.getSimilarity(value, comparisonValue);
|
||||
|
||||
// System.out.println(String.format("%s vs %s = %f", value, comparisonValue, similarity));
|
||||
|
||||
if (similarity > maxSimilarity) {
|
||||
maxSimilarity = similarity;
|
||||
mostSimilar = comparisonValue;
|
||||
}
|
||||
}
|
||||
|
||||
return mostSimilar;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ import org.junit.Test;
|
|||
public class TVRageClientTest {
|
||||
|
||||
private TVRageClient tvrage = new TVRageClient();
|
||||
private TVRageSearchResult testResult = new TVRageSearchResult("Buffy the Vampire Slayer", "2930", "http://www.tvrage.com/Buffy_The_Vampire_Slayer");
|
||||
private TVRageSearchResult testResult = new TVRageSearchResult("Buffy the Vampire Slayer", 2930, "http://www.tvrage.com/Buffy_The_Vampire_Slayer");
|
||||
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
package net.sourceforge.filebot;
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import junit.framework.JUnit4TestAdapter;
|
||||
|
@ -11,11 +11,11 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( {})
|
||||
public class TestSuite {
|
||||
@SuiteClasses( { TVRageClientTest.class })
|
||||
public class WebTestSuite {
|
||||
|
||||
public static Test suite() {
|
||||
return new JUnit4TestAdapter(TestSuite.class);
|
||||
return new JUnit4TestAdapter(WebTestSuite.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ package net.sourceforge.tuned;
|
|||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
|
@ -23,7 +24,7 @@ public class PreferencesListTest {
|
|||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() throws Exception {
|
||||
root = Preferences.userRoot().node("filebot-test/PreferencesList");
|
||||
root = Preferences.userRoot().node("junit-test");
|
||||
|
||||
strings = root.node("strings");
|
||||
strings.put("0", "Rei");
|
||||
|
@ -59,7 +60,7 @@ public class PreferencesListTest {
|
|||
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
public void add() {
|
||||
List<Integer> list = PreferencesList.map(numbers, Integer.class);
|
||||
|
||||
list.add(3);
|
||||
|
@ -70,16 +71,25 @@ public class PreferencesListTest {
|
|||
|
||||
@Test
|
||||
public void remove() {
|
||||
temp.put("0", "Gladiator 1");
|
||||
temp.put("1", "Gladiator 2");
|
||||
temp.put("2", "Gladiator 3");
|
||||
temp.put("3", "Gladiator 4");
|
||||
|
||||
List<String> list = PreferencesList.map(temp, String.class);
|
||||
ArrayList<String> compareValues = new ArrayList<String>();
|
||||
|
||||
compareValues.add("Gladiator 1");
|
||||
compareValues.add("Gladiator 2");
|
||||
compareValues.add("Gladiator 3");
|
||||
compareValues.add("Gladiator 4");
|
||||
compareValues.add("Gladiator 5");
|
||||
|
||||
List<String> prefs = PreferencesList.map(temp, String.class);
|
||||
prefs.addAll(compareValues);
|
||||
|
||||
for (int index : new int[] { 4, 0, 1 }) {
|
||||
prefs.remove(index);
|
||||
compareValues.remove(index);
|
||||
|
||||
assertArrayEquals(compareValues.toArray(), prefs.toArray());
|
||||
}
|
||||
|
||||
assertEquals("Gladiator 2", list.remove(1));
|
||||
assertEquals("Gladiator 4", list.remove(2));
|
||||
assertEquals("Gladiator 1", list.remove(0));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ public class PreferencesMapTest {
|
|||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() throws Exception {
|
||||
root = Preferences.userRoot().node("filebot-test/PreferencesMap");
|
||||
root = Preferences.userRoot().node("junit-test");
|
||||
|
||||
strings = root.node("strings");
|
||||
strings.put("1", "Firefly");
|
||||
|
|
|
@ -11,16 +11,14 @@ import java.util.List;
|
|||
|
||||
public class TestUtil {
|
||||
|
||||
public static <T> List<List<T>> rotations(Collection<T> source) {
|
||||
List<List<T>> rotations = new ArrayList<List<T>>();
|
||||
public static List<Object[]> asParameters(Object... parameterSet) {
|
||||
List<Object[]> list = new ArrayList<Object[]>();
|
||||
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
List<T> copy = new ArrayList<T>(source);
|
||||
Collections.rotate(copy, i);
|
||||
rotations.add(copy);
|
||||
for (Object parameter : parameterSet) {
|
||||
list.add(new Object[] { parameter });
|
||||
}
|
||||
|
||||
return rotations;
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,13 +33,16 @@ public class TestUtil {
|
|||
}
|
||||
|
||||
|
||||
public static List<Object[]> asParameters(Object... parameterSet) {
|
||||
List<Object[]> list = new ArrayList<Object[]>();
|
||||
public static <T> List<List<T>> rotations(Collection<T> source) {
|
||||
List<List<T>> rotations = new ArrayList<List<T>>();
|
||||
|
||||
for (Object parameter : parameterSet) {
|
||||
list.add(new Object[] { parameter });
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
List<T> copy = new ArrayList<T>(source);
|
||||
Collections.rotate(copy, i);
|
||||
rotations.add(copy);
|
||||
}
|
||||
|
||||
return list;
|
||||
return rotations;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( { FunctionIteratorTest.class, PreferencesMapTest.class, PreferencesListTest.class })
|
||||
public class TestSuite {
|
||||
public class TunedTestSuite {
|
||||
|
||||
public static Test suite() {
|
||||
return new JUnit4TestAdapter(TestSuite.class);
|
||||
return new JUnit4TestAdapter(TunedTestSuite.class);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue