Ticket #21794: 21794.2.patch
File 21794.2.patch, 8.0 KB (added by , 2 years ago) |
---|
-
new file src/org/openstreetmap/josm/tools/PatternUtils.java
diff --git a/src/org/openstreetmap/josm/tools/PatternUtils.java b/src/org/openstreetmap/josm/tools/PatternUtils.java new file mode 100644 index 0000000000..b1e41a7873
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import java.util.concurrent.TimeUnit; 5 import java.util.regex.Pattern; 6 7 import org.apache.commons.jcs3.access.CacheAccess; 8 import org.apache.commons.jcs3.engine.behavior.IElementAttributes; 9 import org.openstreetmap.josm.data.cache.JCSCacheManager; 10 import org.openstreetmap.josm.spi.preferences.Config; 11 12 /** 13 * A class that caches compiled patterns. 14 * @author Taylor Smock 15 * @since xxx 16 */ 17 public final class PatternUtils { 18 /** A string that is highly unlikely to appear in regexes to split a regex from its flags */ 19 private static final String MAGIC_STRING = "========"; 20 /** A cache for Java Patterns (no flags) */ 21 private static final CacheAccess<String, Pattern> cache = JCSCacheManager.getCache("java:pattern", 22 Config.getPref().getInt("java.pattern.cache", 1024), 0, null); 23 24 static { 25 // We don't want to keep these around forever, so set a reasonablish max idle life. 26 final IElementAttributes defaultAttributes = cache.getDefaultElementAttributes(); 27 defaultAttributes.setIdleTime(TimeUnit.HOURS.toSeconds(1)); 28 cache.setDefaultElementAttributes(defaultAttributes); 29 } 30 31 private PatternUtils() { 32 // Hide the constructor 33 } 34 35 /** 36 * Compile a regex into a pattern. This may return a {@link Pattern} used elsewhere. This is safe. 37 * @param regex The regex to compile 38 * @return The immutable {@link Pattern}. 39 * @see Pattern#compile(String) 40 */ 41 public static Pattern compile(final String regex) { 42 return compile(regex, 0); 43 } 44 45 /** 46 * Compile a regex into a pattern. This may return a {@link Pattern} used elsewhere. This is safe. 47 * @param regex The regex to compile 48 * @param flags The flags from {@link Pattern} to apply 49 * @return The immutable {@link Pattern}. 50 * @see Pattern#compile(String, int) 51 */ 52 public static Pattern compile(String regex, int flags) { 53 // Right now, the maximum value of flags is 511 (3 characters). This should avoid unnecessary array copying. 54 final StringBuilder sb = new StringBuilder(3 + MAGIC_STRING.length() + regex.length()); 55 return cache.get(sb.append(flags).append(MAGIC_STRING).append(regex).toString(), () -> Pattern.compile(regex, flags)); 56 } 57 } -
src/org/openstreetmap/josm/tools/Tag2Link.java
diff --git a/src/org/openstreetmap/josm/tools/Tag2Link.java b/src/org/openstreetmap/josm/tools/Tag2Link.java index e4c91b1fbf..136b5d96b6 100644
a b import java.net.MalformedURLException; 10 10 import java.net.URL; 11 11 import java.util.Arrays; 12 12 import java.util.Collections; 13 import java.util. HashMap;13 import java.util.EnumMap; 14 14 import java.util.List; 15 15 import java.util.Map; 16 16 import java.util.Objects; 17 17 import java.util.Optional; 18 import java.util.Set; 18 19 import java.util.function.Supplier; 19 20 import java.util.function.UnaryOperator; 20 21 import java.util.regex.Matcher; … … public final class Tag2Link { 147 148 return; 148 149 } 149 150 150 final HashMap<OsmPrimitiveType, Optional<ImageResource>> memoize = new HashMap<>();151 final Map<OsmPrimitiveType, Optional<ImageResource>> memoize = new EnumMap<>(OsmPrimitiveType.class); 151 152 final Supplier<ImageResource> imageResource = () -> memoize 152 153 .computeIfAbsent(OsmPrimitiveType.NODE, type -> OsmPrimitiveImageProvider.getResource(key, value, type)) 153 154 .orElse(null); … … public final class Tag2Link { 208 209 tr("View category on Wikimedia Commons"), getWikimediaCommonsUrl(i), imageResource.get())); 209 210 } 210 211 211 wikidataRules.getValues(key).forEach(urlFormatter -> { 212 final Set<String> formatterUrls = wikidataRules.getValues(key); 213 if (!formatterUrls.isEmpty()) { 212 214 final String formattedValue = valueFormatter.getOrDefault(key, x -> x).apply(value); 213 final String url = urlFormatter.replace("$1", formattedValue); 214 linkConsumer.acceptLink(getLinkName(url, key), url, imageResource.get()); 215 }); 215 final Pattern dollarOne = PatternUtils.compile("$1", Pattern.LITERAL); 216 217 final String urlKey = formatterUrls.stream().map(urlFormatter -> dollarOne.matcher(urlFormatter) 218 .replaceAll(Matcher.quoteReplacement("(.*)"))).map(PatternUtils::compile) 219 .map(pattern -> pattern.matcher(value)).filter(Matcher::matches) 220 .map(matcher -> matcher.group(1)).findFirst().orElse(formattedValue); 221 222 formatterUrls.forEach(urlFormatter -> { 223 // Check if the current value matches the formatter pattern -- some keys can take a full url or a key for 224 // the formatter. Example: https://wiki.openstreetmap.org/wiki/Key:contact:facebook 225 final String url = urlFormatter.replace("$1", urlKey); 226 linkConsumer.acceptLink(getLinkName(url, key), url, imageResource.get()); 227 }); 228 } 216 229 } 217 230 218 231 private static String getWikimediaCommonsUrl(String i) { -
test/unit/org/openstreetmap/josm/tools/Tag2LinkTest.java
diff --git a/test/unit/org/openstreetmap/josm/tools/Tag2LinkTest.java b/test/unit/org/openstreetmap/josm/tools/Tag2LinkTest.java index 6a30f09619..780b9e8bd3 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.tools; 3 3 4 import static org.junit.jupiter.api.Assertions.assertEquals; 5 import static org.junit.jupiter.api.Assertions.assertTrue; 6 4 7 import java.util.ArrayList; 5 8 import java.util.Arrays; 6 9 import java.util.List; 7 10 8 import org.junit.Assert;9 11 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.params.ParameterizedTest; 13 import org.junit.jupiter.params.provider.ValueSource; 10 14 import org.openstreetmap.josm.testutils.annotations.BasicPreferences; 15 import org.openstreetmap.josm.testutils.annotations.StaticClassCleanup; 11 16 12 17 /** 13 18 * Test {@link Tag2Link} 14 19 */ 15 20 @BasicPreferences 21 @StaticClassCleanup(Tag2Link.class) 16 22 class Tag2LinkTest { 17 23 18 24 List<String> links = new ArrayList<>(); … … class Tag2LinkTest { 22 28 } 23 29 24 30 void checkLinks(String... expected) { 25 Assert.assertEquals(Arrays.asList(expected),links);31 assertEquals(Arrays.asList(expected), this.links); 26 32 } 27 33 28 34 /** … … class Tag2LinkTest { 31 37 @Test 32 38 void testInitialize() { 33 39 Tag2Link.initialize(); 34 Assert.assertTrue("obtains at least 40 rules", Tag2Link.wikidataRules.size() > 40); 40 assertTrue(Tag2Link.wikidataRules.size() > 40, "obtains at least 40 rules"); 41 } 42 43 /** 44 * Unit test for links that may come in multiple forms. 45 * Example: <a href="https://wiki.osm.org/wiki/Key:contact:facebook">https://wiki.openstreetmap.org/wiki/Key:contact:facebook</a> 46 * 47 * See also JOSM #21794 48 * @param value The tag value for "contact:facebook" 49 */ 50 @ParameterizedTest 51 @ValueSource(strings = {"https://www.facebook.com/FacebookUserName", "FacebookUserName"}) 52 void testUrlKeyMultipleForms(final String value) { 53 // We need the wikidata rules Since testInitialize tests initialization, reuse it. 54 if (!Tag2Link.wikidataRules.containsKey("contact:facebook")) { 55 this.testInitialize(); 56 } 57 Tag2Link.getLinksForTag("contact:facebook", value, this::addLink); 58 this.checkLinks("Open unavatar.now.sh // https://unavatar.now.sh/facebook/FacebookUserName", 59 "Open facebook.com // https://www.facebook.com/FacebookUserName", 60 "Open messenger.com // https://www.messenger.com/t/FacebookUserName"); 35 61 } 36 62 37 63 /**