diff --git a/OsmAnd-java/src/main/java/net/osmand/CollatorStringMatcher.java b/OsmAnd-java/src/main/java/net/osmand/CollatorStringMatcher.java index b8f66f4878c..b4122846ae1 100644 --- a/OsmAnd-java/src/main/java/net/osmand/CollatorStringMatcher.java +++ b/OsmAnd-java/src/main/java/net/osmand/CollatorStringMatcher.java @@ -1,5 +1,7 @@ package net.osmand; +import net.osmand.util.ArabicNormalizer; + import java.util.Locale; @@ -55,9 +57,14 @@ public Collator getCollator() { public boolean matches(String name) { return cmatches(collator, name, part, mode); } - - - public static boolean cmatches(Collator collator, String fullName, String part, StringMatcherMode mode){ + + public static boolean cmatches(Collator collator, String fullName, String part, StringMatcherMode mode) { + if (ArabicNormalizer.isSpecialArabic(fullName)) { + fullName = ArabicNormalizer.normalize(fullName); + } + if (ArabicNormalizer.isSpecialArabic(part)) { + part = ArabicNormalizer.normalize(part); + } switch (mode) { case CHECK_CONTAINS: return ccontains(collator, fullName, part); diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java index f7732849a07..047ec3144f8 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java @@ -38,12 +38,8 @@ import net.osmand.search.SearchUICore.SearchResultMatcher; import net.osmand.search.core.SearchPhrase.NameStringMatcher; import net.osmand.search.core.SearchPhrase.SearchPhraseDataType; -import net.osmand.util.Algorithms; -import net.osmand.util.GeoPointParserUtil; -import net.osmand.util.GeoParsedPoint; -import net.osmand.util.LocationParser; +import net.osmand.util.*; import net.osmand.util.LocationParser.ParsedOpenLocationCode; -import net.osmand.util.MapUtils; import java.io.IOException; import java.text.DecimalFormat; @@ -556,6 +552,9 @@ public boolean isCancelled() { Iterator offlineIterator = phrase.getRadiusOfflineIndexes(DEFAULT_ADDRESS_BBOX_RADIUS * 5, SearchPhraseDataType.ADDRESS); String wordToSearch = phrase.getUnknownWordToSearch(); + if (ArabicNormalizer.isSpecialArabic(wordToSearch)) { + wordToSearch = ArabicNormalizer.normalize(wordToSearch); + } while (offlineIterator.hasNext() && wordToSearch.length() > 0) { BinaryMapIndexReader r = offlineIterator.next(); currentFile[0] = r; @@ -609,6 +608,9 @@ public boolean search(final SearchPhrase phrase, final SearchResultMatcher resul Iterator offlineIterator = phrase.getRadiusOfflineIndexes(BBOX_RADIUS, SearchPhraseDataType.POI); String searchWord = phrase.getUnknownWordToSearch(); + if (ArabicNormalizer.isSpecialArabic(searchWord)) { + searchWord = ArabicNormalizer.normalize(searchWord); + } final NameStringMatcher nm = phrase.getMainUnknownNameStringMatcher(); QuadRect bbox = phrase.getFileRequest() != null ? phrase.getRadiusBBoxToSearch(BBOX_RADIUS_POI_IN_CITY) : phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE); final Set ids = new HashSet(); diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java index ee69c321db1..05b36da16df 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java @@ -16,6 +16,7 @@ import net.osmand.data.QuadRect; import net.osmand.osm.AbstractPoiType; import net.osmand.util.Algorithms; +import net.osmand.util.ArabicNormalizer; import net.osmand.util.LocationParser; import net.osmand.util.MapUtils; diff --git a/OsmAnd-java/src/main/java/net/osmand/util/ArabicNormalizer.java b/OsmAnd-java/src/main/java/net/osmand/util/ArabicNormalizer.java new file mode 100644 index 00000000000..1e80f424c45 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/util/ArabicNormalizer.java @@ -0,0 +1,67 @@ +package net.osmand.util; + +public class ArabicNormalizer { + + private static final String DIACRITIC_REGEX = "[\\u064B-\\u0652]"; + private static final String ARABIC_DIGITS = "٠١٢٣٤٥٦٧٨٩"; + private static final String DIGITS_REPLACEMENT = "0123456789"; + private static final String KASHIDA = "\u0640"; + + public static boolean isSpecialArabic(String text) { + if (text == null || text.isEmpty()) { + return false; + } + char first = text.charAt(0); + if (Character.UnicodeBlock.of(first) == Character.UnicodeBlock.ARABIC) { + for (char c : text.toCharArray()) { + if (isDiacritic(c) || isArabicDigit(c) || isKashida(c)) { + return true; + } + } + } + return false; + } + + public static String normalize(String text) { + if (text == null || text.isEmpty()) { + return text; + } + String result = text.replaceAll(DIACRITIC_REGEX, ""); + result = result.replace(KASHIDA, ""); + return replaceDigits(result); + } + + private static String replaceDigits(String text) { + if (text == null) { + return null; // Handle null input + } + char first = text.charAt(0); + if (Character.UnicodeBlock.of(first) != Character.UnicodeBlock.ARABIC) { + return text; + } + + char[] textChars = text.toCharArray(); + for (int i = 0; i < ARABIC_DIGITS.length(); i++) { + char c = ARABIC_DIGITS.charAt(i); + char replacement = DIGITS_REPLACEMENT.charAt(i); + int index = text.indexOf(c); + while (index >= 0) { + textChars[index] = replacement; + index = text.indexOf(c, index + 1); + } + } + return String.valueOf(textChars); + } + + private static boolean isDiacritic(char c) { + return c >= '\u064B' && c <= '\u0652'; // Diacritic range + } + + private static boolean isArabicDigit(char c) { + return c >= '\u0660' && c <= '\u0669'; // Arabic-Indic digits ٠-٩ + } + + private static boolean isKashida(char c) { + return c == '\u0640'; // Kashida character + } +}