mirror of
https://github.com/TrianguloY/UrlChecker.git
synced 2024-09-19 20:02:16 +02:00
Merge pull request #243 from the-blank-x/optional-groups
Pattern Checker: Fix optional groups giving a null instead of an empty string
This commit is contained in:
commit
75f282e8bf
@ -16,9 +16,9 @@ import com.trianguloy.urlchecker.url.UrlData;
|
||||
import com.trianguloy.urlchecker.utilities.AndroidUtils;
|
||||
import com.trianguloy.urlchecker.utilities.Inflater;
|
||||
import com.trianguloy.urlchecker.utilities.JavaUtils;
|
||||
import com.trianguloy.urlchecker.utilities.RegexFix;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -73,6 +73,7 @@ class PatternConfig extends AModuleConfig {
|
||||
R.string.mPttrn_userContent,
|
||||
"https://github.com/TrianguloY/UrlChecker/wiki/Custom-patterns"
|
||||
));
|
||||
RegexFix.attachSetting(views.findViewById(R.id.regex_fix));
|
||||
}
|
||||
|
||||
}
|
||||
@ -84,12 +85,14 @@ class PatternDialog extends AModuleDialog {
|
||||
private LinearLayout box;
|
||||
|
||||
private final PatternCatalog catalog;
|
||||
private final RegexFix regexFix;
|
||||
|
||||
private final List<Message> messages = new ArrayList<>();
|
||||
|
||||
public PatternDialog(MainDialog dialog) {
|
||||
super(dialog);
|
||||
catalog = new PatternCatalog(dialog);
|
||||
regexFix = new RegexFix(dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,14 +110,14 @@ class PatternDialog extends AModuleDialog {
|
||||
public void onModifyUrl(UrlData urlData, JavaUtils.Function<UrlData, Boolean> setNewUrl) {
|
||||
// init
|
||||
messages.clear();
|
||||
String url = urlData.url;
|
||||
var url = urlData.url;
|
||||
|
||||
// check each pattern
|
||||
JSONObject patterns = catalog.getCatalog();
|
||||
for (String pattern : JavaUtils.toList(patterns.keys())) {
|
||||
var patterns = catalog.getCatalog();
|
||||
for (var pattern : JavaUtils.toList(patterns.keys())) {
|
||||
try {
|
||||
JSONObject data = patterns.optJSONObject(pattern);
|
||||
Message message = new Message(pattern);
|
||||
var data = patterns.optJSONObject(pattern);
|
||||
var message = new Message(pattern);
|
||||
|
||||
// enabled?
|
||||
if (data == null) continue;
|
||||
@ -122,7 +125,7 @@ class PatternDialog extends AModuleDialog {
|
||||
|
||||
// get regex (must exists)
|
||||
if (!data.has("regex")) continue;
|
||||
var regex = data.getString("regex");
|
||||
var regex_matcher = Pattern.compile(data.getString("regex")).matcher(url);
|
||||
|
||||
// applied?
|
||||
message.applied = urlData.getData(APPLIED + pattern) != null;
|
||||
@ -130,7 +133,7 @@ class PatternDialog extends AModuleDialog {
|
||||
// check matches
|
||||
// if 'regexp' matches, the pattern can match
|
||||
// if 'regexp' doesn't match, the patter doesn't match
|
||||
var matches = Pattern.compile(regex).matcher(url).find();
|
||||
var matches = regex_matcher.find();
|
||||
if (matches && data.has("excludeRegex")) {
|
||||
// if 'excludeRegex' doesn't exist, the pattern can match
|
||||
// if 'excludeRegex' matches, the pattern doesn't matches
|
||||
@ -143,12 +146,12 @@ class PatternDialog extends AModuleDialog {
|
||||
// check replacements
|
||||
String replacement = null;
|
||||
|
||||
Object replacements = data.opt("replacement");
|
||||
var replacements = data.opt("replacement");
|
||||
if (replacements != null) {
|
||||
// data exists
|
||||
if (replacements instanceof JSONArray) {
|
||||
// array, get random
|
||||
JSONArray replacementsArray = (JSONArray) replacements;
|
||||
var replacementsArray = (JSONArray) replacements;
|
||||
replacement = replacementsArray.getString(new Random().nextInt(replacementsArray.length()));
|
||||
} else {
|
||||
// single data, get that one
|
||||
@ -158,7 +161,7 @@ class PatternDialog extends AModuleDialog {
|
||||
|
||||
if (replacement != null) {
|
||||
// replace url
|
||||
message.newUrl = url.replaceAll(regex, replacement);
|
||||
message.newUrl = regexFix.replaceAll(url, regex_matcher, replacement);
|
||||
|
||||
// automatic? apply
|
||||
if (data.optBoolean("automatic")) {
|
||||
|
@ -0,0 +1,137 @@
|
||||
package com.trianguloy.urlchecker.utilities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.widget.Switch;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* On Android 10 and under, optional groups may yield a "null" in the replacement output instead of an empty string.
|
||||
* Therefore, we just copy the implementation from a newer version of Android
|
||||
* https://github.com/TrianguloY/UrlChecker/issues/237
|
||||
*/
|
||||
public class RegexFix {
|
||||
|
||||
/**
|
||||
* Android 11 and up have already the fix, disable in those cases
|
||||
*/
|
||||
public static final boolean IS_ANDROID_FIXED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
|
||||
|
||||
private final GenericPref.Bool pttrn_regexfix;
|
||||
|
||||
public RegexFix(Context cntx) {
|
||||
// fix enabled by default
|
||||
pttrn_regexfix = new GenericPref.Bool("pttrn_regexfix", true, cntx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the setting to a given switch view (or disabled if not needed)
|
||||
*/
|
||||
public static void attachSetting(Switch view) {
|
||||
if (IS_ANDROID_FIXED) {
|
||||
// hide, already native
|
||||
view.setVisibility(View.GONE);
|
||||
} else {
|
||||
// show, required
|
||||
new RegexFix(view.getContext()).pttrn_regexfix.attachToSwitch(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this instead of: matcher = pattern.matcher(text); result = matcher.replaceAll(replacement)
|
||||
*/
|
||||
public String replaceAll(String text, Matcher matcher, String replacement) {
|
||||
if (IS_ANDROID_FIXED || !pttrn_regexfix.get()) {
|
||||
// no fix required or explicitly disabled, just use native function
|
||||
return matcher.replaceAll(replacement);
|
||||
}
|
||||
|
||||
// Copied from https://android.googlesource.com/platform/libcore/+/refs/heads/android13-release/ojluni/src/main/java/java/util/regex/Matcher.java#837
|
||||
boolean result = matcher.reset().find();
|
||||
if (result) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int appendPos = 0;
|
||||
do {
|
||||
appendPos = appendReplacement(text, matcher, sb, replacement, appendPos);
|
||||
result = matcher.find();
|
||||
} while (result);
|
||||
appendTail(text, sb, appendPos);
|
||||
return sb.toString();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static int appendReplacement(String text, Matcher matcher, StringBuffer sb, String replacement, int appendPos) {
|
||||
// Copied from https://android.googlesource.com/platform/libcore/+/refs/heads/android13-release/ojluni/src/main/java/java/util/regex/Matcher.java#714
|
||||
sb.append(text.substring(appendPos, matcher.start()));
|
||||
appendEvaluated(matcher, sb, replacement);
|
||||
return matcher.end();
|
||||
}
|
||||
|
||||
private static void appendEvaluated(Matcher matcher, StringBuffer buffer, String s) {
|
||||
// Copied from https://android.googlesource.com/platform/libcore/+/refs/heads/android13-release/ojluni/src/main/java/java/util/regex/Matcher.java#731
|
||||
boolean escape = false;
|
||||
boolean dollar = false;
|
||||
boolean escapeNamedGroup = false;
|
||||
int escapeNamedGroupStart = -1;
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '\\' && !escape) {
|
||||
escape = true;
|
||||
} else if (c == '$' && !escape) {
|
||||
dollar = true;
|
||||
} else if (c >= '0' && c <= '9' && dollar && !escapeNamedGroup) {
|
||||
String groupValue = matcher.group(c - '0');
|
||||
if (groupValue != null) {
|
||||
buffer.append(groupValue);
|
||||
}
|
||||
dollar = false;
|
||||
} else if (c == '{' && dollar) {
|
||||
escapeNamedGroup = true;
|
||||
escapeNamedGroupStart = i;
|
||||
} else if (c == '}' && dollar && escapeNamedGroup) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
throw new IllegalArgumentException("your android version does not support named-capturing groups");
|
||||
}
|
||||
String groupValue = matcher.group(s.substring(escapeNamedGroupStart + 1, i));
|
||||
if (groupValue != null) {
|
||||
buffer.append(groupValue);
|
||||
}
|
||||
dollar = false;
|
||||
escapeNamedGroup = false;
|
||||
} else if (c != '}' && dollar && escapeNamedGroup) {
|
||||
continue;
|
||||
} else {
|
||||
buffer.append(c);
|
||||
dollar = false;
|
||||
escape = false;
|
||||
escapeNamedGroup = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (escape) {
|
||||
throw new IllegalArgumentException("character to be escaped is missing");
|
||||
}
|
||||
|
||||
if (dollar) {
|
||||
throw new IllegalArgumentException("Illegal group reference: group index is missing");
|
||||
}
|
||||
|
||||
if (escapeNamedGroup) {
|
||||
throw new IllegalArgumentException("Missing ending brace '}' from replacement string");
|
||||
}
|
||||
}
|
||||
|
||||
private static StringBuffer appendTail(String text, StringBuffer sb, int appendPos) {
|
||||
// Copied from https://android.googlesource.com/platform/libcore/+/refs/heads/android13-release/ojluni/src/main/java/java/util/regex/Matcher.java#796
|
||||
int to = text.length();
|
||||
if (appendPos < to) {
|
||||
sb.append(text.substring(appendPos, to));
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
}
|
@ -16,6 +16,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/json_edit" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/regex_fix"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mPttrn_regexfix" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/user_content"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -119,6 +119,7 @@ Built-in patterns include:
|
||||
- Warning when contains non-ascii characters like greek letters. This can be used for phishing: googĺe.com vs google.com
|
||||
- Suggest replacing 'http' with 'https'
|
||||
- Suggest replacing Youtube, Reddit or Twitter with privacy-friendly alternatives [disabled by default]"</string>
|
||||
<string name="mPttrn_regexfix">Fix optional regex groups (disable if regex is not working correctly)</string>
|
||||
<string name="mPttrn_userContent">List of other useful user-created patterns: %s</string>
|
||||
<string name="mPttrn_fix">Apply</string>
|
||||
<string name="mPttrn_fixed">Applied - %s</string>
|
||||
|
Loading…
Reference in New Issue
Block a user