i2psnark: Add "smart sort" option, set sort based on language (tickets #637, #1303)

This commit is contained in:
zzz
2015-10-16 19:45:23 +00:00
parent 39b218b216
commit ba1488bcce
5 changed files with 175 additions and 13 deletions

View File

@@ -126,6 +126,7 @@ public class SnarkManager implements CompleteListener {
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers"; public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers"; public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers";
private static final String PROP_USE_DHT = "i2psnark.enableDHT"; private static final String PROP_USE_DHT = "i2psnark.enableDHT";
private static final String PROP_SMART_SORT = "i2psnark.smartSort";
public static final int MIN_UP_BW = 10; public static final int MIN_UP_BW = 10;
public static final int DEFAULT_MAX_UP_BW = 25; public static final int DEFAULT_MAX_UP_BW = 25;
@@ -340,6 +341,17 @@ public class SnarkManager implements CompleteListener {
public boolean shouldAutoStart() { public boolean shouldAutoStart() {
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START)); return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START));
} }
/**
* @return default true
* @since 0.9.23
*/
public boolean isSmartSortEnabled() {
String val = _config.getProperty(PROP_SMART_SORT);
if (val == null)
return true;
return Boolean.parseBoolean(val);
}
/**** /****
public String linkPrefix() { public String linkPrefix() {
@@ -736,19 +748,19 @@ public class SnarkManager implements CompleteListener {
/** /**
* all params may be null or need trimming * all params may be null or need trimming
*/ */
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay, public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, boolean smartSort, String refreshDelay,
String startDelay, String pageSize, String seedPct, String eepHost, String startDelay, String pageSize, String seedPct, String eepHost,
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) { String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
synchronized(_configLock) { synchronized(_configLock) {
locked_updateConfig(dataDir, filesPublic, autoStart, refreshDelay, locked_updateConfig(dataDir, filesPublic, autoStart, smartSort,refreshDelay,
startDelay, pageSize, seedPct, eepHost, startDelay, pageSize, seedPct, eepHost,
eepPort, i2cpHost, i2cpPort, i2cpOpts, eepPort, i2cpHost, i2cpPort, i2cpOpts,
upLimit, upBW, useOpenTrackers, useDHT, theme); upLimit, upBW, useOpenTrackers, useDHT, theme);
} }
} }
private void locked_updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay, private void locked_updateConfig(String dataDir, boolean filesPublic, boolean autoStart, boolean smartSort, String refreshDelay,
String startDelay, String pageSize, String seedPct, String eepHost, String startDelay, String pageSize, String seedPct, String eepHost,
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) { String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
@@ -966,6 +978,16 @@ public class SnarkManager implements CompleteListener {
addMessage(_t("Disabled autostart")); addMessage(_t("Disabled autostart"));
changed = true; changed = true;
} }
if (isSmartSortEnabled() != smartSort) {
_config.setProperty(PROP_SMART_SORT, Boolean.toString(smartSort));
if (smartSort)
addMessage(_t("Enabled smart sort"));
else
addMessage(_t("Disabled smart sort"));
changed = true;
}
if (_util.shouldUseOpenTrackers() != useOpenTrackers) { if (_util.shouldUseOpenTrackers() != useOpenTrackers) {
_config.setProperty(PROP_USE_OPENTRACKERS, useOpenTrackers + ""); _config.setProperty(PROP_USE_OPENTRACKERS, useOpenTrackers + "");
if (useOpenTrackers) if (useOpenTrackers)

View File

@@ -32,6 +32,7 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.SecureFile; import net.i2p.util.SecureFile;
import net.i2p.util.Translate;
import org.klomp.snark.I2PSnarkUtil; import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.MagnetURI; import org.klomp.snark.MagnetURI;
@@ -1118,6 +1119,7 @@ public class I2PSnarkServlet extends BasicServlet {
String dataDir = req.getParameter("nofilter_dataDir"); String dataDir = req.getParameter("nofilter_dataDir");
boolean filesPublic = req.getParameter("filesPublic") != null; boolean filesPublic = req.getParameter("filesPublic") != null;
boolean autoStart = req.getParameter("autoStart") != null; boolean autoStart = req.getParameter("autoStart") != null;
boolean smartSort = req.getParameter("smartSort") != null;
String seedPct = req.getParameter("seedPct"); String seedPct = req.getParameter("seedPct");
String eepHost = req.getParameter("eepHost"); String eepHost = req.getParameter("eepHost");
String eepPort = req.getParameter("eepPort"); String eepPort = req.getParameter("eepPort");
@@ -1133,7 +1135,7 @@ public class I2PSnarkServlet extends BasicServlet {
boolean useDHT = req.getParameter("useDHT") != null; boolean useDHT = req.getParameter("useDHT") != null;
//String openTrackers = req.getParameter("openTrackers"); //String openTrackers = req.getParameter("openTrackers");
String theme = req.getParameter("theme"); String theme = req.getParameter("theme");
_manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel, pageSize, _manager.updateConfig(dataDir, filesPublic, autoStart, smartSort, refreshDel, startupDel, pageSize,
seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts,
upLimit, upBW, useOpenTrackers, useDHT, theme); upLimit, upBW, useOpenTrackers, useDHT, theme);
// update servlet // update servlet
@@ -1401,6 +1403,10 @@ public class I2PSnarkServlet extends BasicServlet {
sort = Integer.parseInt(ssort); sort = Integer.parseInt(ssort);
} catch (NumberFormatException nfe) {} } catch (NumberFormatException nfe) {}
} }
if (_manager.isSmartSortEnabled())
Sorters.setPattern(Translate.getLanguage(_manager.util().getContext()));
else
Sorters.setPattern(null);
try { try {
Collections.sort(rv, Sorters.getComparator(sort, this)); Collections.sort(rv, Sorters.getComparator(sort, this));
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
@@ -2144,6 +2150,7 @@ public class I2PSnarkServlet extends BasicServlet {
String dataDir = _manager.getDataDir().getAbsolutePath(); String dataDir = _manager.getDataDir().getAbsolutePath();
boolean filesPublic = _manager.areFilesPublic(); boolean filesPublic = _manager.areFilesPublic();
boolean autoStart = _manager.shouldAutoStart(); boolean autoStart = _manager.shouldAutoStart();
boolean smartSort = _manager.isSmartSortEnabled();
boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers(); boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers();
//String openTrackers = _manager.util().getOpenTrackerString(); //String openTrackers = _manager.util().getOpenTrackerString();
boolean useDHT = _manager.util().shouldUseDHT(); boolean useDHT = _manager.util().shouldUseDHT();
@@ -2177,6 +2184,14 @@ public class I2PSnarkServlet extends BasicServlet {
+ (autoStart ? "checked " : "") + (autoStart ? "checked " : "")
+ "title=\""); + "title=\"");
out.write(_t("If checked, automatically start torrents that are added")); out.write(_t("If checked, automatically start torrents that are added"));
out.write("\" >" +
"<tr><td>");
out.write(_t("Smart torrent sorting"));
out.write(": <td><input type=\"checkbox\" class=\"optbox\" name=\"smartSort\" value=\"true\" "
+ (smartSort ? "checked " : "")
+ "title=\"");
out.write(_t("If checked, ignore words such as 'the' when sorting"));
out.write("\" >" + out.write("\" >" +
"<tr><td>"); "<tr><td>");

View File

@@ -6,6 +6,8 @@ import java.text.Collator;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.klomp.snark.MetaInfo; import org.klomp.snark.MetaInfo;
import org.klomp.snark.Snark; import org.klomp.snark.Snark;
@@ -18,6 +20,13 @@ import org.klomp.snark.Storage;
*/ */
class Sorters { class Sorters {
/**
* See below
*/
private static final Pattern PATTERN_DE, PATTERN_EN, PATTERN_ES, PATTERN_FR,
PATTERN_IT, PATTERN_NL, PATTERN_PT;
private static Pattern _pattern;
/** /**
* Negative is reverse * Negative is reverse
* *
@@ -113,8 +122,8 @@ class Sorters {
/** /**
* Sort alphabetically in current locale, ignore case, ignore leading "the " * Sort alphabetically in current locale, ignore case, ignore leading
* (I guess this is worth it, a lot of torrents start with "The " * articles such as "the" if the pattern is set by setPattern()
* @since 0.7.14 * @since 0.7.14
*/ */
private static class TorrentNameComparator implements Comparator<Snark>, Serializable { private static class TorrentNameComparator implements Comparator<Snark>, Serializable {
@@ -130,13 +139,16 @@ class Sorters {
if (l.getStorage() != null && r.getStorage() == null) if (l.getStorage() != null && r.getStorage() == null)
return 1; return 1;
String ls = l.getBaseName(); String ls = l.getBaseName();
String llc = ls.toLowerCase(Locale.US);
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
ls = ls.substring(4);
String rs = r.getBaseName(); String rs = r.getBaseName();
String rlc = rs.toLowerCase(Locale.US); Pattern p = _pattern;
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_")) if (p != null) {
rs = rs.substring(4); Matcher m = p.matcher(ls);
if (m.matches())
ls = ls.substring(m.group(1).length());
m = p.matcher(rs);
if (m.matches())
rs = rs.substring(m.group(1).length());
}
return Collator.getInstance().compare(ls, rs); return Collator.getInstance().compare(ls, rs);
} }
} }
@@ -528,4 +540,104 @@ class Sorters {
return r.priority - l.priority; return r.priority - l.priority;
} }
} }
/*
* Match an indefinite or definite article in the language,
* followed by one or more whitespace, '.', or '_'.
* Does not match "partitive" articles.
*
* https://en.wikipedia.org/wiki/Article_%28grammar%29
* http://www.loc.gov/marc/bibliographic/bdapndxf.html
*/
static {
PATTERN_DE = Pattern.compile(
// can't make the non-capturing innner group work
//"^((?:" +
"^((" +
"der|die|das|des|dem|den|ein|eine|einer|eines|einem|einen" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_EN = Pattern.compile(
"^((" +
"a|an|the" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_ES = Pattern.compile(
"^((" +
"el|la|lo|los|las|un|una|unos|unas" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_FR = Pattern.compile(
// note l' doesn't require whitespace after
"^(l'|((" +
"le|la|les|un|une|des" +
")[\\s\\._]+)).*",
Pattern.CASE_INSENSITIVE);
PATTERN_IT = Pattern.compile(
// note l' and un' don't require whitespace after
"^(l'|un'|((" +
"il|lo|la|i|gli|le|uno|una|un" +
")[\\s\\._]+)).*",
Pattern.CASE_INSENSITIVE);
PATTERN_NL = Pattern.compile(
"^((" +
"de|het|het'n|een|een'n" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_PT = Pattern.compile(
"^((" +
"o|a|os|as|um|uma|uns|umas" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
}
/**
* Sets static field, oh well
* @param lang null for none
* @since 0.9.23
*/
public static void setPattern(String lang) {
Pattern p;
if (lang == null)
p = null;
else if (lang.equals("de"))
p = PATTERN_DE;
else if (lang.equals("en"))
p = PATTERN_EN;
else if (lang.equals("es"))
p = PATTERN_ES;
else if (lang.equals("fr"))
p = PATTERN_FR;
else if (lang.equals("it"))
p = PATTERN_IT;
else if (lang.equals("nl"))
p = PATTERN_NL;
else if (lang.equals("pt"))
p = PATTERN_PT;
else
p = null;
_pattern = p;
}
/****
public static final void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: Sorters lang 'string'");
System.exit(1);
}
String lang = args[0];
setPattern(lang);
if (_pattern == null) {
System.out.println("Unsupported " + lang);
System.exit(1);
}
String s = args[1];
Matcher m = _pattern.matcher(s);
if (m.matches()) {
System.out.println("Match is \"" + m.group(1) + '"');
} else {
System.out.println("No match for \"" + s + '"');
}
}
****/
} }

View File

@@ -1,3 +1,16 @@
2015-10-16 zzz
* i2psnark:
- Fix deadlock (ticket #1432)
- Add "smart sort" option, set sort based on language (tickets #637, #1303)
2015-10-14 zzz
* Update:
- Require Java 7 to download dev builds (ticket #1669)
- Fix persistence of the available dev version
2015-10-13 zzz
* Startup: Delete our old RI from netDB when rekeying
2015-10-11 zzz 2015-10-11 zzz
* Crypto: Test for broken Gentoo ECDSA support * Crypto: Test for broken Gentoo ECDSA support

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 17; public final static long BUILD = 18;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";