diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 98d0ee2c0b..bb1684e5f5 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -126,6 +126,7 @@ public class SnarkManager implements CompleteListener { public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers"; public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers"; 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 DEFAULT_MAX_UP_BW = 25; @@ -340,6 +341,17 @@ public class SnarkManager implements CompleteListener { public boolean shouldAutoStart() { 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() { @@ -736,19 +748,19 @@ public class SnarkManager implements CompleteListener { /** * 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 eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) { synchronized(_configLock) { - locked_updateConfig(dataDir, filesPublic, autoStart, refreshDelay, + locked_updateConfig(dataDir, filesPublic, autoStart, smartSort,refreshDelay, startDelay, pageSize, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, 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 eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) { @@ -966,6 +978,16 @@ public class SnarkManager implements CompleteListener { addMessage(_t("Disabled autostart")); 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) { _config.setProperty(PROP_USE_OPENTRACKERS, useOpenTrackers + ""); if (useOpenTrackers) diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 913fd6d77c..b0335f6a8c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -32,6 +32,7 @@ import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.util.Log; import net.i2p.util.SecureFile; +import net.i2p.util.Translate; import org.klomp.snark.I2PSnarkUtil; import org.klomp.snark.MagnetURI; @@ -1118,6 +1119,7 @@ public class I2PSnarkServlet extends BasicServlet { String dataDir = req.getParameter("nofilter_dataDir"); boolean filesPublic = req.getParameter("filesPublic") != null; boolean autoStart = req.getParameter("autoStart") != null; + boolean smartSort = req.getParameter("smartSort") != null; String seedPct = req.getParameter("seedPct"); String eepHost = req.getParameter("eepHost"); String eepPort = req.getParameter("eepPort"); @@ -1133,7 +1135,7 @@ public class I2PSnarkServlet extends BasicServlet { boolean useDHT = req.getParameter("useDHT") != null; //String openTrackers = req.getParameter("openTrackers"); 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, upLimit, upBW, useOpenTrackers, useDHT, theme); // update servlet @@ -1401,6 +1403,10 @@ public class I2PSnarkServlet extends BasicServlet { sort = Integer.parseInt(ssort); } catch (NumberFormatException nfe) {} } + if (_manager.isSmartSortEnabled()) + Sorters.setPattern(Translate.getLanguage(_manager.util().getContext())); + else + Sorters.setPattern(null); try { Collections.sort(rv, Sorters.getComparator(sort, this)); } catch (IllegalArgumentException iae) { @@ -2144,6 +2150,7 @@ public class I2PSnarkServlet extends BasicServlet { String dataDir = _manager.getDataDir().getAbsolutePath(); boolean filesPublic = _manager.areFilesPublic(); boolean autoStart = _manager.shouldAutoStart(); + boolean smartSort = _manager.isSmartSortEnabled(); boolean useOpenTrackers = _manager.util().shouldUseOpenTrackers(); //String openTrackers = _manager.util().getOpenTrackerString(); boolean useDHT = _manager.util().shouldUseDHT(); @@ -2177,6 +2184,14 @@ public class I2PSnarkServlet extends BasicServlet { + (autoStart ? "checked " : "") + "title=\""); out.write(_t("If checked, automatically start torrents that are added")); + out.write("\" >" + + + ""); + out.write(_t("Smart torrent sorting")); + out.write(": " + ""); diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java b/apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java index 39120d6cce..964e6d80d2 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java @@ -6,6 +6,8 @@ import java.text.Collator; import java.util.Collections; import java.util.Comparator; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.klomp.snark.MetaInfo; import org.klomp.snark.Snark; @@ -18,6 +20,13 @@ import org.klomp.snark.Storage; */ 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 * @@ -113,8 +122,8 @@ class Sorters { /** - * Sort alphabetically in current locale, ignore case, ignore leading "the " - * (I guess this is worth it, a lot of torrents start with "The " + * Sort alphabetically in current locale, ignore case, ignore leading + * articles such as "the" if the pattern is set by setPattern() * @since 0.7.14 */ private static class TorrentNameComparator implements Comparator, Serializable { @@ -130,13 +139,16 @@ class Sorters { if (l.getStorage() != null && r.getStorage() == null) return 1; 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 rlc = rs.toLowerCase(Locale.US); - if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_")) - rs = rs.substring(4); + Pattern p = _pattern; + if (p != null) { + 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); } } @@ -528,4 +540,104 @@ class Sorters { 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 + '"'); + } + } +****/ } diff --git a/history.txt b/history.txt index 80b38233e6..f04c70cac8 100644 --- a/history.txt +++ b/history.txt @@ -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 * Crypto: Test for broken Gentoo ECDSA support diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 8ab24107c9..80cfb9c849 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 17; + public final static long BUILD = 18; /** for example "-test" */ public final static String EXTRA = "";