mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Better search implementation, kotlinize, start remove clone
This commit is contained in:
@@ -21,25 +21,25 @@ package com.kunzisoft.keepass.tests.utils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.kunzisoft.keepass.utils.StrUtil;
|
||||
import com.kunzisoft.keepass.utils.StringUtil;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class StrUtilTest extends TestCase {
|
||||
public class StringUtilTest extends TestCase {
|
||||
private final String text = "AbCdEfGhIj";
|
||||
private final String search = "BcDe";
|
||||
private final String badSearch = "Ed";
|
||||
|
||||
public void testIndexOfIgnoreCase1() {
|
||||
assertEquals(1, StrUtil.indexOfIgnoreCase(text, search, Locale.ENGLISH));
|
||||
assertEquals(1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public void testIndexOfIgnoreCase2() {
|
||||
assertEquals(-1, StrUtil.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2);
|
||||
assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2);
|
||||
}
|
||||
|
||||
public void testIndexOfIgnoreCase3() {
|
||||
assertEquals(-1, StrUtil.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH));
|
||||
assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH));
|
||||
}
|
||||
|
||||
private final String repText = "AbCtestingaBc";
|
||||
@@ -48,10 +48,10 @@ public class StrUtilTest extends TestCase {
|
||||
private final String repNew = "12345";
|
||||
private final String repResult = "12345testing12345";
|
||||
public void testReplaceAllIgnoresCase1() {
|
||||
assertEquals(repResult, StrUtil.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH));
|
||||
assertEquals(repResult, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public void testReplaceAllIgnoresCase2() {
|
||||
assertEquals(repText, StrUtil.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH));
|
||||
assertEquals(repText, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ package com.kunzisoft.keepass.compat;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
|
||||
import com.kunzisoft.keepass.utils.StrUtil;
|
||||
import com.kunzisoft.keepass.utils.StringUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -66,7 +66,7 @@ public final class PRNGFixes {
|
||||
|
||||
private static boolean supportedOnThisDevice() {
|
||||
// Blacklist on samsung devices
|
||||
if (StrUtil.indexOfIgnoreCase(Build.MANUFACTURER, "samsung", Locale.ENGLISH) >= 0) {
|
||||
if (StringUtil.INSTANCE.indexOfIgnoreCase(Build.MANUFACTURER, "samsung", Locale.ENGLISH) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,13 @@ public class NativeAESCipherSpi extends CipherSpi {
|
||||
|
||||
@Override
|
||||
protected byte[] engineGetIV() {
|
||||
return mIV.clone();
|
||||
byte[] copyIV = new byte[0];
|
||||
if (mIV != null) {
|
||||
int lengthIV = mIV.length;
|
||||
copyIV = new byte[lengthIV];
|
||||
System.arraycopy(mIV, 0, copyIV, 0, lengthIV);
|
||||
}
|
||||
return copyIV;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.Map;
|
||||
|
||||
import static com.kunzisoft.keepass.database.element.PwEntryV4.*;
|
||||
|
||||
public class ExtraFields implements Parcelable, Cloneable {
|
||||
public class ExtraFields implements Parcelable {
|
||||
|
||||
private Map<String, ProtectedString> fields;
|
||||
|
||||
@@ -151,25 +151,4 @@ public class ExtraFields implements Parcelable, Cloneable {
|
||||
&& !key.equals(STR_PASSWORD) && !key.equals(STR_URL)
|
||||
&& !key.equals(STR_NOTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtraFields clone() {
|
||||
try {
|
||||
ExtraFields clone = (ExtraFields) super.clone();
|
||||
clone.fields = copyMap(this.fields);
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, ProtectedString> copyMap(
|
||||
Map<String, ProtectedString> original) {
|
||||
HashMap<String, ProtectedString> copy = new HashMap<>();
|
||||
for (Map.Entry<String, ProtectedString> entry : original.entrySet()) {
|
||||
copy.put(entry.getKey(), new ProtectedString(entry.getValue()));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +145,17 @@ public class PwEntryV3 extends PwEntry<PwGroupV3, PwEntryV3> {
|
||||
super.updateWith(source);
|
||||
title = source.title;
|
||||
username = source.username;
|
||||
int passLen = source.password.length;
|
||||
password = new byte[passLen];
|
||||
System.arraycopy(source.password, 0, password, 0, passLen);
|
||||
|
||||
if (source.password != null) {
|
||||
int passLen = source.password.length;
|
||||
password = new byte[passLen];
|
||||
System.arraycopy(source.password, 0, password, 0, passLen);
|
||||
}
|
||||
|
||||
url = source.url;
|
||||
additional = source.additional;
|
||||
|
||||
binaryDesc = source.binaryDesc;
|
||||
|
||||
if ( source.binaryData != null ) {
|
||||
int descLen = source.binaryData.length;
|
||||
binaryData = new byte[descLen];
|
||||
|
||||
@@ -172,7 +172,7 @@ public class PwEntryV4 extends PwEntry<PwGroupV4, PwEntryV4> implements ITimeLog
|
||||
// newEntry.usageCount stay the same in copy
|
||||
newEntry.parentGroupLastMod = this.parentGroupLastMod.clone();
|
||||
|
||||
newEntry.fields = this.fields.clone();
|
||||
newEntry.fields = new ExtraFields(this.fields);
|
||||
// TODO customData make copy from hashmap
|
||||
newEntry.binaries = (HashMap<String, ProtectedBinary>) this.binaries.clone();
|
||||
// newEntry.foregroundColor stay the same in copy
|
||||
@@ -204,20 +204,22 @@ public class PwEntryV4 extends PwEntry<PwGroupV4, PwEntryV4> implements ITimeLog
|
||||
this.mDecodeRef = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a reference key woth the SprEngineV4
|
||||
* @param decodeRef
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private String decodeRefKey(boolean decodeRef, String key) {
|
||||
String text = getProtectedStringValue(key);
|
||||
if (decodeRef) {
|
||||
text = decodeRef(text, mDatabase);
|
||||
if (mDatabase == null)
|
||||
return text;
|
||||
return new SprEngineV4().compile(text, this, mDatabase);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private String decodeRef(String text, PwDatabase db) {
|
||||
if (db == null) { return text; }
|
||||
SprEngineV4 spr = new SprEngineV4();
|
||||
return spr.compile(text, this, db);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getUsername() {
|
||||
|
||||
@@ -19,23 +19,4 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.iterator
|
||||
|
||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||
|
||||
abstract class EntrySearchStringIterator : Iterator<String> {
|
||||
|
||||
companion object {
|
||||
|
||||
fun getInstance(entry: EntryVersioned): EntrySearchStringIterator {
|
||||
if (entry.pwEntryV3 != null) {
|
||||
return EntrySearchStringIteratorV3(entry.pwEntryV3)
|
||||
}
|
||||
if (entry.pwEntryV4 != null) {
|
||||
return EntrySearchStringIteratorV4(entry.pwEntryV4!!)
|
||||
}
|
||||
|
||||
throw RuntimeException("This should not be possible")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
abstract class EntrySearchStringIterator : Iterator<String>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
@@ -25,18 +25,17 @@ import com.kunzisoft.keepass.database.search.SearchParameters;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator {
|
||||
|
||||
private PwEntryV3 entry;
|
||||
private SearchParameters sp;
|
||||
|
||||
|
||||
private PwEntryV3 mEntry;
|
||||
private SearchParameters mSearchParameters;
|
||||
|
||||
public EntrySearchStringIteratorV3(PwEntryV3 entry) {
|
||||
this.entry = entry;
|
||||
this.sp = SearchParameters.DEFAULT;
|
||||
this(entry, new SearchParameters());
|
||||
}
|
||||
|
||||
public EntrySearchStringIteratorV3(PwEntryV3 entry, SearchParameters sp) {
|
||||
this.entry = entry;
|
||||
this.sp = sp;
|
||||
public EntrySearchStringIteratorV3(PwEntryV3 entry, SearchParameters searchParameters) {
|
||||
this.mEntry = entry;
|
||||
this.mSearchParameters = searchParameters;
|
||||
}
|
||||
|
||||
private static final int title = 0;
|
||||
@@ -44,76 +43,67 @@ public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator {
|
||||
private static final int username = 2;
|
||||
private static final int comment = 3;
|
||||
private static final int maxEntries = 4;
|
||||
|
||||
|
||||
private int current = 0;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current < maxEntries;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
// Past the end of the list
|
||||
if (current == maxEntries) {
|
||||
throw new NoSuchElementException("Past final string");
|
||||
}
|
||||
|
||||
|
||||
useSearchParameters();
|
||||
|
||||
|
||||
String str = getCurrentString();
|
||||
current++;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
private void useSearchParameters() {
|
||||
|
||||
if (sp == null) { return; }
|
||||
|
||||
if (mSearchParameters == null) { return; }
|
||||
|
||||
boolean found = false;
|
||||
|
||||
while (!found) {
|
||||
switch (current) {
|
||||
case title:
|
||||
found = sp.searchInTitles;
|
||||
found = mSearchParameters.getSearchInTitles();
|
||||
break;
|
||||
|
||||
case url:
|
||||
found = sp.searchInUrls;
|
||||
found = mSearchParameters.getSearchInUrls();
|
||||
break;
|
||||
|
||||
case username:
|
||||
found = sp.searchInUserNames;
|
||||
found = mSearchParameters.getSearchInUserNames();
|
||||
break;
|
||||
|
||||
case comment:
|
||||
found = sp.searchInNotes;
|
||||
found = mSearchParameters.getSearchInNotes();
|
||||
break;
|
||||
|
||||
default:
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found) { current++; }
|
||||
|
||||
if (!found) { current++; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getCurrentString() {
|
||||
switch (current) {
|
||||
case title:
|
||||
return entry.getTitle();
|
||||
|
||||
case url:
|
||||
return entry.getUrl();
|
||||
|
||||
case username:
|
||||
return entry.getUsername();
|
||||
|
||||
case comment:
|
||||
return entry.getNotes();
|
||||
|
||||
default:
|
||||
return "";
|
||||
case title:
|
||||
return mEntry.getTitle();
|
||||
case url:
|
||||
return mEntry.getUrl();
|
||||
case username:
|
||||
return mEntry.getUsername();
|
||||
case comment:
|
||||
return mEntry.getNotes();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,60 +27,60 @@ import kotlin.collections.Map.Entry
|
||||
|
||||
class EntrySearchStringIteratorV4 : EntrySearchStringIterator {
|
||||
|
||||
private var current: String? = null
|
||||
private var setIterator: Iterator<Entry<String, ProtectedString>>? = null
|
||||
private var sp: SearchParametersV4? = null
|
||||
private var mCurrent: String? = null
|
||||
private var mSetIterator: Iterator<Entry<String, ProtectedString>>? = null
|
||||
private var mSearchParametersV4: SearchParametersV4? = null
|
||||
|
||||
constructor(entry: PwEntryV4) {
|
||||
this.sp = SearchParametersV4.DEFAULT
|
||||
setIterator = entry.fields.listOfAllFields.entries.iterator()
|
||||
this.mSearchParametersV4 = SearchParametersV4()
|
||||
mSetIterator = entry.fields.listOfAllFields.entries.iterator()
|
||||
advance()
|
||||
}
|
||||
|
||||
constructor(entry: PwEntryV4, sp: SearchParametersV4) {
|
||||
this.sp = sp
|
||||
setIterator = entry.fields.listOfAllFields.entries.iterator()
|
||||
constructor(entry: PwEntryV4, searchParametersV4: SearchParametersV4) {
|
||||
this.mSearchParametersV4 = searchParametersV4
|
||||
mSetIterator = entry.fields.listOfAllFields.entries.iterator()
|
||||
advance()
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return current != null
|
||||
return mCurrent != null
|
||||
}
|
||||
|
||||
override fun next(): String {
|
||||
if (current == null) {
|
||||
if (mCurrent == null) {
|
||||
throw NoSuchElementException("Past the end of the list.")
|
||||
}
|
||||
|
||||
val next = current
|
||||
val next = mCurrent
|
||||
advance()
|
||||
return next!!
|
||||
}
|
||||
|
||||
private fun advance() {
|
||||
while (setIterator!!.hasNext()) {
|
||||
val entry = setIterator!!.next()
|
||||
mSetIterator?.let {
|
||||
while (it.hasNext()) {
|
||||
val entry = it.next()
|
||||
val key = entry.key
|
||||
|
||||
val key = entry.key
|
||||
|
||||
if (searchInField(key)) {
|
||||
current = entry.value.toString()
|
||||
return
|
||||
if (searchInField(key)) {
|
||||
mCurrent = entry.value.toString()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
current = null
|
||||
mCurrent = null
|
||||
}
|
||||
|
||||
private fun searchInField(key: String): Boolean {
|
||||
when (key) {
|
||||
PwEntryV4.STR_TITLE -> return sp!!.searchInTitles
|
||||
PwEntryV4.STR_USERNAME -> return sp!!.searchInUserNames
|
||||
PwEntryV4.STR_PASSWORD -> return sp!!.searchInPasswords
|
||||
PwEntryV4.STR_URL -> return sp!!.searchInUrls
|
||||
PwEntryV4.STR_NOTES -> return sp!!.searchInNotes
|
||||
else -> return sp!!.searchInOther
|
||||
return when (key) {
|
||||
PwEntryV4.STR_TITLE -> mSearchParametersV4!!.searchInTitles
|
||||
PwEntryV4.STR_USERNAME -> mSearchParametersV4!!.searchInUserNames
|
||||
PwEntryV4.STR_PASSWORD -> mSearchParametersV4!!.searchInPasswords
|
||||
PwEntryV4.STR_URL -> mSearchParametersV4!!.searchInUrls
|
||||
PwEntryV4.STR_NOTES -> mSearchParametersV4!!.searchInNotes
|
||||
else -> mSearchParametersV4!!.searchInOther
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.kunzisoft.keepass.database.element.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.element.PwGroupV4;
|
||||
import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator;
|
||||
import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4;
|
||||
import com.kunzisoft.keepass.utils.StrUtil;
|
||||
import com.kunzisoft.keepass.utils.StringUtil;
|
||||
import com.kunzisoft.keepass.utils.UuidUtil;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -33,53 +33,53 @@ import java.util.Locale;
|
||||
|
||||
public class EntrySearchHandlerV4 extends NodeHandler<PwEntryV4> {
|
||||
|
||||
private List<PwEntryV4> listStorage;
|
||||
protected SearchParametersV4 sp;
|
||||
private List<PwEntryV4> mListStorage;
|
||||
private SearchParametersV4 mSearchParametersV4;
|
||||
protected Date now;
|
||||
|
||||
public EntrySearchHandlerV4(SearchParametersV4 sp, List<PwEntryV4> listStorage) {
|
||||
this.listStorage = listStorage;
|
||||
this.sp = sp;
|
||||
public EntrySearchHandlerV4(SearchParametersV4 searchParametersV4, List<PwEntryV4> listStorage) {
|
||||
this.mListStorage = listStorage;
|
||||
this.mSearchParametersV4 = searchParametersV4;
|
||||
this.now = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntryV4 entry) {
|
||||
if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) {
|
||||
if (mSearchParametersV4.getRespectEntrySearchingDisabled() && !entry.isSearchingEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sp.excludeExpired && entry.isExpires() && now.after(entry.getExpiryTime().getDate())) {
|
||||
if (mSearchParametersV4.getExcludeExpired() && entry.isExpires() && now.after(entry.getExpiryTime().getDate())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String term = sp.searchString;
|
||||
if (sp.ignoreCase) {
|
||||
String term = mSearchParametersV4.getSearchString();
|
||||
if (mSearchParametersV4.getIgnoreCase()) {
|
||||
term = term.toLowerCase();
|
||||
}
|
||||
|
||||
if (searchStrings(entry, term)) {
|
||||
listStorage.add(entry);
|
||||
mListStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sp.searchInGroupNames) {
|
||||
if (mSearchParametersV4.getSearchInGroupNames()) {
|
||||
PwGroupV4 parent = entry.getParent();
|
||||
if (parent != null) {
|
||||
String groupName = parent.getTitle();
|
||||
if (sp.ignoreCase) {
|
||||
if (mSearchParametersV4.getIgnoreCase()) {
|
||||
groupName = groupName.toLowerCase();
|
||||
}
|
||||
|
||||
if (groupName.contains(term)) {
|
||||
listStorage.add(entry);
|
||||
mListStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (searchID(entry)) {
|
||||
listStorage.add(entry);
|
||||
mListStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -87,20 +87,20 @@ public class EntrySearchHandlerV4 extends NodeHandler<PwEntryV4> {
|
||||
}
|
||||
|
||||
private boolean searchID(PwEntryV4 entry) {
|
||||
if (sp.searchInUUIDs) {
|
||||
if (mSearchParametersV4.getSearchInUUIDs()) {
|
||||
String hex = UuidUtil.toHexString(entry.getNodeId().getId());
|
||||
return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0;
|
||||
return StringUtil.INSTANCE.indexOfIgnoreCase(hex, mSearchParametersV4.getSearchString(), Locale.ENGLISH) >= 0;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean searchStrings(PwEntryV4 entry, String term) {
|
||||
EntrySearchStringIterator iter = new EntrySearchStringIteratorV4(entry, sp);
|
||||
while (iter.hasNext()) {
|
||||
String str = iter.next();
|
||||
EntrySearchStringIterator iterator = new EntrySearchStringIteratorV4(entry, mSearchParametersV4);
|
||||
while (iterator.hasNext()) {
|
||||
String str = iterator.next();
|
||||
if (str.length() > 0) {
|
||||
if (sp.ignoreCase) {
|
||||
if (mSearchParametersV4.getIgnoreCase()) {
|
||||
str = str.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||
import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator
|
||||
import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV3
|
||||
import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4
|
||||
import java.util.*
|
||||
|
||||
class SearchDbHelper(private val isOmitBackup: Boolean) {
|
||||
@@ -72,13 +74,23 @@ class SearchDbHelper(private val isOmitBackup: Boolean) {
|
||||
|
||||
private fun entryContainsString(entry: EntryVersioned, qStr: String, loc: Locale): Boolean {
|
||||
// Search all strings in the entry
|
||||
val iterator = EntrySearchStringIterator.getInstance(entry)
|
||||
while (iterator.hasNext()) {
|
||||
val str = iterator.next()
|
||||
if (str.isNotEmpty()) {
|
||||
val lower = str.toLowerCase(loc)
|
||||
if (lower.contains(qStr)) {
|
||||
return true
|
||||
|
||||
var iterator: EntrySearchStringIterator? = null
|
||||
entry.pwEntryV3?.let {
|
||||
iterator = EntrySearchStringIteratorV3(it)
|
||||
}
|
||||
entry.pwEntryV4?.let {
|
||||
iterator = EntrySearchStringIteratorV4(it)
|
||||
}
|
||||
|
||||
iterator?.let {
|
||||
while (it.hasNext()) {
|
||||
val str = it.next()
|
||||
if (str.isNotEmpty()) {
|
||||
val lower = str.toLowerCase(loc)
|
||||
if (lower.contains(qStr)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.search;
|
||||
|
||||
/**
|
||||
* @author bpellin
|
||||
* Parameters for searching strings in the database.
|
||||
*
|
||||
*/
|
||||
public class SearchParameters implements Cloneable {
|
||||
public static final SearchParameters DEFAULT = new SearchParameters();
|
||||
|
||||
public String searchString;
|
||||
|
||||
public boolean regularExpression = false;
|
||||
public boolean searchInTitles = true;
|
||||
public boolean searchInUserNames = true;
|
||||
public boolean searchInPasswords = false;
|
||||
public boolean searchInUrls = true;
|
||||
public boolean searchInGroupNames = false;
|
||||
public boolean searchInNotes = true;
|
||||
public boolean ignoreCase = true;
|
||||
public boolean ignoreExpired = false;
|
||||
public boolean respectEntrySearchingDisabled = true;
|
||||
public boolean excludeExpired = false;
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setupNone() {
|
||||
searchInTitles = false;
|
||||
searchInUserNames = false;
|
||||
searchInPasswords = false;
|
||||
searchInUrls = false;
|
||||
searchInGroupNames = false;
|
||||
searchInNotes = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.search
|
||||
|
||||
/**
|
||||
* Parameters for searching strings in the database.
|
||||
*/
|
||||
open class SearchParameters {
|
||||
|
||||
var searchString: String = ""
|
||||
|
||||
var regularExpression = false
|
||||
var searchInTitles = true
|
||||
var searchInUserNames = true
|
||||
var searchInPasswords = false
|
||||
var searchInUrls = true
|
||||
var searchInGroupNames = false
|
||||
var searchInNotes = true
|
||||
var ignoreCase = true
|
||||
var ignoreExpired = false
|
||||
var respectEntrySearchingDisabled = true
|
||||
var excludeExpired = false
|
||||
|
||||
constructor()
|
||||
|
||||
/**
|
||||
* Copy search parameters
|
||||
* @param source
|
||||
*/
|
||||
constructor(source: SearchParameters) {
|
||||
regularExpression = source.regularExpression
|
||||
searchInTitles = source.searchInTitles
|
||||
searchInUserNames = source.searchInUserNames
|
||||
searchInPasswords = source.searchInPasswords
|
||||
searchInUrls = source.searchInUrls
|
||||
searchInGroupNames = source.searchInGroupNames
|
||||
searchInNotes = source.searchInNotes
|
||||
ignoreCase = source.ignoreCase
|
||||
ignoreExpired = source.ignoreExpired
|
||||
respectEntrySearchingDisabled = source.respectEntrySearchingDisabled
|
||||
excludeExpired = source.excludeExpired
|
||||
}
|
||||
|
||||
open fun setupNone() {
|
||||
searchInTitles = false
|
||||
searchInUserNames = false
|
||||
searchInPasswords = false
|
||||
searchInUrls = false
|
||||
searchInGroupNames = false
|
||||
searchInNotes = false
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.search;
|
||||
|
||||
public class SearchParametersV4 extends SearchParameters implements Cloneable {
|
||||
public static SearchParametersV4 DEFAULT = new SearchParametersV4();
|
||||
|
||||
public boolean searchInOther = true;
|
||||
public boolean searchInUUIDs = false;
|
||||
public boolean searchInTags = true;
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupNone() {
|
||||
super.setupNone();
|
||||
searchInOther = false;
|
||||
searchInUUIDs = false;
|
||||
searchInTags = false;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
@@ -17,31 +17,26 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.utils;
|
||||
package com.kunzisoft.keepass.database.search
|
||||
|
||||
import com.kunzisoft.keepass.database.element.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.element.PwEntryV4;
|
||||
class SearchParametersV4 : SearchParameters {
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
var searchInOther = true
|
||||
var searchInUUIDs = false
|
||||
var searchInTags = true
|
||||
|
||||
public class SprContextV4 implements Cloneable {
|
||||
constructor() : super()
|
||||
|
||||
public PwDatabaseV4 db;
|
||||
public PwEntryV4 entry;
|
||||
public Map<String, String> refsCache = new HashMap<>();
|
||||
|
||||
public SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) {
|
||||
this.db = db;
|
||||
this.entry = entry;
|
||||
}
|
||||
constructor(searchParametersV4: SearchParametersV4) : super(searchParametersV4) {
|
||||
this.searchInOther = searchParametersV4.searchInOther
|
||||
this.searchInUUIDs = searchParametersV4.searchInUUIDs
|
||||
this.searchInTags = searchParametersV4.searchInTags
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
override fun setupNone() {
|
||||
super.setupNone()
|
||||
searchInOther = false
|
||||
searchInUUIDs = false
|
||||
searchInTags = false
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
@@ -37,47 +37,65 @@ public class SprEngineV4 {
|
||||
public class TargetResult {
|
||||
public PwEntryV4 entry;
|
||||
public char wanted;
|
||||
|
||||
|
||||
public TargetResult(PwEntryV4 entry, char wanted) {
|
||||
this.entry = entry;
|
||||
this.wanted = wanted;
|
||||
}
|
||||
}
|
||||
|
||||
private class SprContextV4 {
|
||||
|
||||
public PwDatabaseV4 db;
|
||||
public PwEntryV4 entry;
|
||||
public Map<String, String> refsCache = new HashMap<>();
|
||||
|
||||
SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) {
|
||||
this.db = db;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
SprContextV4(SprContextV4 source) {
|
||||
this.db = source.db;
|
||||
this.entry = source.entry;
|
||||
this.refsCache = source.refsCache;
|
||||
}
|
||||
}
|
||||
|
||||
public String compile(String text, PwEntryV4 entry, PwDatabase database) {
|
||||
SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry);
|
||||
|
||||
|
||||
return compileInternal(text, ctx, 0);
|
||||
}
|
||||
|
||||
private String compileInternal(String text, SprContextV4 ctx, int recursionLevel) {
|
||||
|
||||
private String compileInternal(String text, SprContextV4 sprContextV4, int recursionLevel) {
|
||||
if (text == null) { return ""; }
|
||||
if (ctx == null) { return ""; }
|
||||
if (sprContextV4 == null) { return ""; }
|
||||
if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; }
|
||||
|
||||
return fillRefPlaceholders(text, ctx, recursionLevel);
|
||||
|
||||
return fillRefPlaceholders(text, sprContextV4, recursionLevel);
|
||||
}
|
||||
|
||||
private String fillRefPlaceholders(String text, SprContextV4 ctx, int recursionLevel) {
|
||||
|
||||
if (ctx.db == null) { return text; }
|
||||
|
||||
|
||||
private String fillRefPlaceholders(String text, SprContextV4 contextV4, int recursionLevel) {
|
||||
|
||||
if (contextV4.db == null) { return text; }
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
text = fillRefsUsingCache(text, ctx);
|
||||
|
||||
int start = StrUtil.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH);
|
||||
text = fillRefsUsingCache(text, contextV4);
|
||||
|
||||
int start = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH);
|
||||
if (start < 0) { break; }
|
||||
int end = StrUtil.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH);
|
||||
int end = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH);
|
||||
if (end <= start) { break; }
|
||||
|
||||
|
||||
String fullRef = text.substring(start, end - start + 1);
|
||||
TargetResult result = findRefTarget(fullRef, ctx);
|
||||
|
||||
TargetResult result = findRefTarget(fullRef, contextV4);
|
||||
|
||||
if (result != null) {
|
||||
PwEntryV4 found = result.entry;
|
||||
char wanted = result.wanted;
|
||||
|
||||
|
||||
if (found != null) {
|
||||
String data;
|
||||
switch (wanted) {
|
||||
@@ -103,92 +121,88 @@ public class SprEngineV4 {
|
||||
offset = start + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
SprContextV4 subCtx = (SprContextV4) ctx.clone();
|
||||
|
||||
SprContextV4 subCtx = new SprContextV4(contextV4);
|
||||
subCtx.entry = found;
|
||||
|
||||
|
||||
String innerContent = compileInternal(data, subCtx, recursionLevel + 1);
|
||||
addRefsToCache(fullRef, innerContent, ctx);
|
||||
text = fillRefsUsingCache(text, ctx);
|
||||
addRefsToCache(fullRef, innerContent, contextV4);
|
||||
text = fillRefsUsingCache(text, contextV4);
|
||||
} else {
|
||||
offset = start + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public TargetResult findRefTarget(String fullRef, SprContextV4 ctx) {
|
||||
private TargetResult findRefTarget(String fullRef, SprContextV4 contextV4) {
|
||||
if (fullRef == null) { return null; }
|
||||
|
||||
|
||||
fullRef = fullRef.toUpperCase(Locale.ENGLISH);
|
||||
if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) {
|
||||
if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
String ref = fullRef.substring(STR_REF_START.length(), fullRef.length() - STR_REF_START.length() - STR_REF_END.length());
|
||||
if (ref.length() <= 4) { return null; }
|
||||
if (ref.charAt(1) != '@') { return null; }
|
||||
if (ref.charAt(3) != ':') { return null; }
|
||||
|
||||
char scan = Character.MIN_VALUE;
|
||||
char wanted = Character.MIN_VALUE;
|
||||
|
||||
scan = Character.toUpperCase(ref.charAt(2));
|
||||
wanted = Character.toUpperCase(ref.charAt(0));
|
||||
|
||||
SearchParametersV4 sp = new SearchParametersV4();
|
||||
sp.setupNone();
|
||||
|
||||
sp.searchString = ref.substring(4);
|
||||
if (scan == 'T') { sp.searchInTitles = true; }
|
||||
else if (scan == 'U') { sp.searchInUserNames = true; }
|
||||
else if (scan == 'A') { sp.searchInUrls = true; }
|
||||
else if (scan == 'P') { sp.searchInPasswords = true; }
|
||||
else if (scan == 'N') { sp.searchInNotes = true; }
|
||||
else if (scan == 'I') { sp.searchInUUIDs = true; }
|
||||
else if (scan == 'O') { sp.searchInOther = true; }
|
||||
char scan = Character.toUpperCase(ref.charAt(2));
|
||||
char wanted = Character.toUpperCase(ref.charAt(0));
|
||||
|
||||
SearchParametersV4 searchParametersV4 = new SearchParametersV4();
|
||||
searchParametersV4.setupNone();
|
||||
|
||||
searchParametersV4.setSearchString(ref.substring(4));
|
||||
if (scan == 'T') { searchParametersV4.setSearchInTitles(true); }
|
||||
else if (scan == 'U') { searchParametersV4.setSearchInUserNames(true); }
|
||||
else if (scan == 'A') { searchParametersV4.setSearchInUrls(true); }
|
||||
else if (scan == 'P') { searchParametersV4.setSearchInPasswords(true); }
|
||||
else if (scan == 'N') { searchParametersV4.setSearchInNotes(true); }
|
||||
else if (scan == 'I') { searchParametersV4.setSearchInUUIDs(true); }
|
||||
else if (scan == 'O') { searchParametersV4.setSearchInOther(true); }
|
||||
else { return null; }
|
||||
|
||||
List<PwEntryV4> list = new ArrayList<>();
|
||||
// TODO type parameter
|
||||
searchEntries(ctx.db.getRootGroup(), sp, list);
|
||||
|
||||
if (list.size() > 0) {
|
||||
searchEntries(contextV4.db.getRootGroup(), searchParametersV4, list);
|
||||
|
||||
if (list.size() > 0) {
|
||||
return new TargetResult(list.get(0), wanted);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void addRefsToCache(String ref, String value, SprContextV4 ctx) {
|
||||
if (ref == null) { return; }
|
||||
if (value == null) { return; }
|
||||
if (ctx == null) { return; }
|
||||
|
||||
|
||||
if (!ctx.refsCache.containsKey(ref)) {
|
||||
ctx.refsCache.put(ref, value);
|
||||
}
|
||||
}
|
||||
|
||||
private String fillRefsUsingCache(String text, SprContextV4 ctx) {
|
||||
for (Entry<String, String> entry : ctx.refsCache.entrySet()) {
|
||||
text = StrUtil.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH);
|
||||
|
||||
private String fillRefsUsingCache(String text, SprContextV4 sprContextV4) {
|
||||
for (Entry<String, String> entry : sprContextV4.refsCache.entrySet()) {
|
||||
text = StringUtil.INSTANCE.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private void searchEntries(PwGroupV4 root, SearchParametersV4 sp, List<PwEntryV4> listStorage) {
|
||||
if (sp == null) { return; }
|
||||
private void searchEntries(PwGroupV4 root, SearchParametersV4 searchParametersV4, List<PwEntryV4> listStorage) {
|
||||
if (searchParametersV4 == null) { return; }
|
||||
if (listStorage == null) { return; }
|
||||
|
||||
List<String> terms = StrUtil.splitSearchTerms(sp.searchString);
|
||||
if (terms.size() <= 1 || sp.regularExpression) {
|
||||
root.doForEachChild(new EntrySearchHandlerV4(sp, listStorage), null);
|
||||
List<String> terms = StringUtil.INSTANCE.splitStringTerms(searchParametersV4.getSearchString());
|
||||
if (terms.size() <= 1 || searchParametersV4.getRegularExpression()) {
|
||||
root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, listStorage), null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -196,41 +210,41 @@ public class SprEngineV4 {
|
||||
Comparator<String> stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length();
|
||||
Collections.sort(terms, stringLengthComparator);
|
||||
|
||||
String fullSearch = sp.searchString;
|
||||
List<PwEntryV4> pg = root.getChildEntries();
|
||||
String fullSearch = searchParametersV4.getSearchString();
|
||||
List<PwEntryV4> childEntries = root.getChildEntries();
|
||||
for (int i = 0; i < terms.size(); i ++) {
|
||||
List<PwEntryV4> pgNew = new ArrayList<>();
|
||||
|
||||
sp.searchString = terms.get(i);
|
||||
searchParametersV4.setSearchString(terms.get(i));
|
||||
|
||||
boolean negate = false;
|
||||
if (sp.searchString.startsWith("-")) {
|
||||
sp.searchString = sp.searchString.substring(1);
|
||||
negate = sp.searchString.length() > 0;
|
||||
if (searchParametersV4.getSearchString().startsWith("-")) {
|
||||
searchParametersV4.setSearchString(searchParametersV4.getSearchString().substring(1));
|
||||
negate = searchParametersV4.getSearchString().length() > 0;
|
||||
}
|
||||
|
||||
if (!root.doForEachChild(new EntrySearchHandlerV4(sp, pgNew), null)) {
|
||||
pg = null;
|
||||
if (!root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, pgNew), null)) {
|
||||
childEntries = null;
|
||||
break;
|
||||
}
|
||||
|
||||
List<PwEntryV4> complement = new ArrayList<>();
|
||||
if (negate) {
|
||||
for (PwEntryV4 entry: pg) {
|
||||
for (PwEntryV4 entry: childEntries) {
|
||||
if (!pgNew.contains(entry)) {
|
||||
complement.add(entry);
|
||||
}
|
||||
}
|
||||
pg = complement;
|
||||
childEntries = complement;
|
||||
}
|
||||
else {
|
||||
pg = pgNew;
|
||||
childEntries = pgNew;
|
||||
}
|
||||
}
|
||||
|
||||
if (pg != null) {
|
||||
listStorage.addAll(pg);
|
||||
if (childEntries != null) {
|
||||
listStorage.addAll(childEntries);
|
||||
}
|
||||
sp.searchString = fullSearch;
|
||||
searchParametersV4.setSearchString(fullSearch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class StrUtil {
|
||||
public static List<String> splitSearchTerms(String search) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
if (search == null) { return list; }
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean quoted = false;
|
||||
|
||||
for (int i = 0; i < search.length(); i++) {
|
||||
char ch = search.charAt(i);
|
||||
|
||||
if ( ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
|
||||
&& !quoted) {
|
||||
|
||||
int len = sb.length();
|
||||
if (len > 0) {
|
||||
list.add(sb.toString());
|
||||
sb.delete(0, len);
|
||||
}
|
||||
else if (ch == '\"') {
|
||||
quoted = !quoted;
|
||||
}
|
||||
else {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.length() > 0) {
|
||||
list.add(sb.toString());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static int indexOfIgnoreCase(String text, String search, int start, Locale locale) {
|
||||
if (text == null || search == null) return -1;
|
||||
|
||||
return text.toLowerCase(locale).indexOf(search.toLowerCase(locale), start);
|
||||
}
|
||||
|
||||
public static int indexOfIgnoreCase(String text, String search, Locale locale) {
|
||||
return indexOfIgnoreCase(text, search, 0, locale);
|
||||
}
|
||||
|
||||
public static String replaceAllIgnoresCase(String text, String find, String newText, Locale locale) {
|
||||
if (text == null || find == null || newText == null) { return text; }
|
||||
|
||||
int pos = 0;
|
||||
while (pos < text.length()) {
|
||||
pos = indexOfIgnoreCase(text, find, pos, locale);
|
||||
if (pos < 0) { break; }
|
||||
|
||||
String before = text.substring(0, pos);
|
||||
String after = text.substring(pos + find.length());
|
||||
|
||||
text = before.concat(newText).concat(after);
|
||||
pos += newText.length();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
95
app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt
Normal file
95
app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.utils
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.Locale
|
||||
|
||||
object StringUtil {
|
||||
|
||||
/**
|
||||
* Create a list of String by split text when ' ', '\t', '\r' or '\n' is found
|
||||
*/
|
||||
fun splitStringTerms(text: String?): List<String> {
|
||||
val list = ArrayList<String>()
|
||||
if (text == null) {
|
||||
return list
|
||||
}
|
||||
|
||||
val stringBuilder = StringBuilder()
|
||||
var quoted = false
|
||||
|
||||
for (i in 0 until text.length) {
|
||||
val ch = text[i]
|
||||
|
||||
if ((ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') && !quoted) {
|
||||
|
||||
val len = stringBuilder.length
|
||||
when {
|
||||
len > 0 -> {
|
||||
list.add(stringBuilder.toString())
|
||||
stringBuilder.delete(0, len)
|
||||
}
|
||||
ch == '\"' -> quoted = !quoted
|
||||
else -> stringBuilder.append(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stringBuilder.isNotEmpty()) {
|
||||
list.add(stringBuilder.toString())
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
fun indexOfIgnoreCase(text: String, search: String, start: Int, locale: Locale): Int {
|
||||
return text.toLowerCase(locale).indexOf(search.toLowerCase(locale), start)
|
||||
|
||||
}
|
||||
|
||||
fun indexOfIgnoreCase(text: String, search: String, locale: Locale): Int {
|
||||
return indexOfIgnoreCase(text, search, 0, locale)
|
||||
}
|
||||
|
||||
fun replaceAllIgnoresCase(text: String?, find: String?, newText: String?, locale: Locale): String? {
|
||||
var currentText = text
|
||||
if (currentText == null || find == null || newText == null) {
|
||||
return currentText
|
||||
}
|
||||
|
||||
var pos = 0
|
||||
while (pos < currentText!!.length) {
|
||||
pos = indexOfIgnoreCase(currentText, find, pos, locale)
|
||||
if (pos < 0) {
|
||||
break
|
||||
}
|
||||
|
||||
val before = currentText.substring(0, pos)
|
||||
val after = currentText.substring(pos + find.length)
|
||||
|
||||
currentText = before + newText + after
|
||||
pos += newText.length
|
||||
}
|
||||
|
||||
return currentText
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user