diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/RegexUtils.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/RegexUtils.kt index d4ec32b37..03c745449 100755 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/RegexUtils.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/RegexUtils.kt @@ -21,15 +21,14 @@ object RegexUtils { /** * find the [Pattern] from cache or compile the regular expression and cache it */ - private fun getPattern(regex: String?, flags: Int = 0): Pattern? { - if (regex == null) return null + private fun getPattern(regex: String, flags: Int = 0): Pattern { val regexWithFlag = RegexWithFlag(regex, flags) var pattern: Pattern? = tryGet(regexWithFlag) if (null == pattern) { pattern = Pattern.compile(regex, flags) cachePattern(regexWithFlag, pattern) } - return pattern + return pattern!! } /** @@ -57,7 +56,7 @@ object RegexUtils { return null } val pattern = getPattern(regex, Pattern.DOTALL) - return get(pattern!!, content, groupIndex) + return get(pattern, content, groupIndex) } private fun get(pattern: Pattern, content: String, groupIndex: Int): String? { @@ -72,13 +71,13 @@ object RegexUtils { * otherwise null if not matched */ fun getAllGroups(pattern: String?, content: String?): List? { + if (pattern == null || content == null) { + return null + } return getAllGroups(getPattern(pattern), content) } - private fun getAllGroups(pattern: Pattern?, content: String?): List? { - if (null == content || null == pattern) { - return null - } + private fun getAllGroups(pattern: Pattern, content: String): List { val result = ArrayList() val matcher = pattern.matcher(content) @@ -96,18 +95,18 @@ object RegexUtils { return null } val pattern = getPattern(regex, Pattern.DOTALL) - return extract(pattern!!, content, template) + return extract(pattern, content, template) } private fun extract(pattern: Pattern, content: String, template: String): String { return if (template.contains("$")) { - extract(pattern, content) { template } - } else { extract(pattern, content) { matcher -> extract(GROUP_VAR, template) { - matcher.group(it.group(0).toInt()) + matcher.group(it.group(1).toInt()) } } + } else { + extract(pattern, content) { template } } } @@ -126,7 +125,7 @@ object RegexUtils { * pattern with the given replacement string. */ fun delFirst(pattern: String, content: String): String { - return delFirst(getPattern(pattern)!!, content) + return delFirst(getPattern(pattern), content) } private fun delFirst(pattern: Pattern, content: String): String { @@ -143,13 +142,13 @@ object RegexUtils { * pattern */ fun delAll(regex: String, content: String): String { - if (StringUtils.isAnyBlank(regex, content)) { + if (StringUtils.isAnyEmpty(regex, content)) { return content } // RegEx pattern = RegEx.compile(regex, RegEx.DOTALL); val pattern = getPattern(regex, Pattern.DOTALL) - return delAll(pattern!!, content) + return delAll(pattern, content) } /** @@ -158,14 +157,11 @@ object RegexUtils { * pattern */ private fun delAll(pattern: Pattern, content: String): String { - return if (content.isBlank()) { - content - } else pattern.matcher(content).replaceAll("") - + return pattern.matcher(content).replaceAll("") } fun delBefore(regex: String, content: String): String? { - val pattern = getPattern(regex, Pattern.DOTALL)!! + val pattern = getPattern(regex, Pattern.DOTALL) val matcher = pattern.matcher(content) return if (matcher.find()) { StringUtils.substring(content, matcher.end() - 1, content.length) @@ -192,19 +188,11 @@ object RegexUtils { } private fun > findAll( - pattern: Pattern?, - content: String?, + pattern: Pattern, + content: String, group: Int, - collection: T?, - ): T? { - if (null == pattern || null == content) { - return null - } - - if (null == collection) { - throw NullPointerException("Null collection param provided!") - } - + collection: T, + ): T { val matcher = pattern.matcher(content) while (matcher.find()) { collection.add(matcher.group(group)) @@ -217,7 +205,7 @@ object RegexUtils { return 0 } val pattern = getPattern(regex, Pattern.DOTALL) - return count(pattern!!, content) + return count(pattern, content) } private fun count(pattern: Pattern, content: String): Int { @@ -235,7 +223,7 @@ object RegexUtils { return false } val pattern = getPattern(regex, Pattern.DOTALL) - return pattern!!.matcher(content).find() + return pattern.matcher(content).find() } fun isMatch(regex: String?, content: String?): Boolean { @@ -243,7 +231,7 @@ object RegexUtils { return false } val pattern = getPattern(regex, Pattern.DOTALL) - return pattern!!.matcher(content).matches() + return pattern.matcher(content).matches() } fun replaceAll(content: String, regex: String, replacementTemplate: String): String? { diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/SimpleStringDiffHelper.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/SimpleStringDiffHelper.kt deleted file mode 100644 index b6089c2ca..000000000 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/SimpleStringDiffHelper.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.itangcent.idea.plugin.utils - -import com.google.common.primitives.Ints -import org.apache.commons.lang3.StringUtils - -/** - * Temporary implement of [StringDiffHelper]. - * It will be optimized and refactor in the future. - * - * Only try check prefix and suffix. - * Diff of strings like 'xaaaaaaaax' and 'yaaaaaaaay' will get a lower score than expected. - */ -class SimpleStringDiffHelper : StringDiffHelper { - override fun diff(str1: String, str2: String): Int { - if (str1 == str2) { - return 0 - } - return if (str1.length * str2.length < THRESHOLD) { - calculateStringDistance(str1.toCharArray(), str2.toCharArray()) - } else { - calculateDiffHasty(str1, str2) - } - } - - private fun calculateDiffHasty(str1: String, str2: String): Int { - val diff1 = StringUtils.indexOfDifference(str1, str2) - if (diff1 == -1) { - return 100 - } - val diff2 = StringUtils.indexOfDifference(str1.reversed(), str2.reversed()) - return 100 - ((diff1 + diff2) * 100) / (str1.length + str2.length) - } - - private fun calculateStringDistance(strA: CharArray, strB: CharArray): Int { - val lenA = strA.size + 1 - val lenB = strB.size + 1 - val c = arrayOfNulls(lenA) - for (i in 0 until lenA) c[i] = IntArray(lenB) - - // Record the distance of all begin points of each string - for (i in 0 until lenA) c[i]!![0] = i - for (j in 0 until lenB) c[0]!![j] = j - c[0]!![0] = 0 - for (i in 1 until lenA) { - for (j in 1 until lenB) { - if (strB[j - 1] == strA[i - 1]) { - c[i]!![j] = c[i - 1]!![j - 1] - } else { - c[i]!![j] = Ints.min(c[i]!![j - 1], c[i - 1]!![j]) + 1 - } - } - } - return (c[lenA - 1]!![lenB - 1] * 100) / (strA.size + strB.size) - } -} - -private const val THRESHOLD = 1024 * 8 \ No newline at end of file diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/Storage.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/Storage.kt index 1b2aae4b8..720d3feef 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/Storage.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/Storage.kt @@ -5,40 +5,95 @@ package com.itangcent.idea.plugin.utils */ interface Storage { + /** + * Get the value of the specified name in the default group + */ fun get(name: String?): Any? + /** + * Get the value of the specified name in the specified group + */ fun get(group: String?, name: String?): Any? + /** + * Set the value of the specified name in the default group + */ fun set(name: String?, value: Any?) + /** + * Set the value of the specified name in the specified group + */ fun set(group: String?, name: String?, value: Any?) + /** + * Pop the value of the specified name in the default group + */ fun pop(name: String?): Any? + /** + * Pop the value of the specified name in the specified group + */ fun pop(group: String?, name: String?): Any? + /** + * Peek the value of the specified name in the default group + */ fun peek(name: String?): Any? + /** + * Peek the value of the specified name in the specified group + */ fun peek(group: String?, name: String?): Any? + /** + * Push the value of the specified name in the default group + */ fun push(name: String?, value: Any?) + /** + * Push the value of the specified name in the specified group + */ fun push(group: String?, name: String?, value: Any?) + /** + * Remove the value of the specified name in the default group + */ fun remove(name: String) + /** + * Remove the value of the specified name in the specified group + */ fun remove(group: String?, name: String) + /** + * Get all keys in the default group + */ fun keys(): Array + /** + * Get all keys in the specified group + */ fun keys(group: String?): Array + /** + * Clear all data in the default group + */ fun clear() + /** + * Clear all data in the specified group + */ fun clear(group: String?) companion object { + /** + * The default group + */ const val DEFAULT_GROUP = "__default_local_group" + + /** + * The null key + */ const val NULL = "__null" } } \ No newline at end of file diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/StringDiffHelper.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/StringDiffHelper.kt deleted file mode 100644 index 048a590db..000000000 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/StringDiffHelper.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.itangcent.idea.plugin.utils - -import com.google.inject.ImplementedBy - -@ImplementedBy(SimpleStringDiffHelper::class) -interface StringDiffHelper { - - /** - * return score from 0-100 - * 100 means completely different - * 0 means same string. - */ - fun diff(str1: String, str2: String): Int -} \ No newline at end of file diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/ThrottleCachedBeanBinder.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/ThrottleCachedBeanBinder.kt deleted file mode 100644 index f8352361d..000000000 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/utils/ThrottleCachedBeanBinder.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.itangcent.idea.plugin.utils - -import com.itangcent.intellij.context.ActionContext -import com.itangcent.intellij.extend.rx.ThrottleHelper -import com.itangcent.intellij.file.BeanBinder - - -class ThrottleCachedBeanBinder : BeanBinder { - private var delegate: BeanBinder - - private var throttle = ThrottleHelper().build(this) - - @Volatile - var cache: T? = null - - constructor(delegate: BeanBinder) { - this.delegate = delegate - } - - override fun tryRead(): T? { - if (cache == null) { - cache = delegate.tryRead() - } - return cache - } - - override fun read(): T { - if (cache == null) { - cache = delegate.read() - } - return cache as T - } - - override fun save(t: T?) { - cache = t - val context = ActionContext.getContext() - if (context == null) { - delegate.save(t) - } else { - if (throttle.acquire(2000)) { - context.runAsync { - Thread.sleep(2100) - cache?.let { cache -> delegate.save(cache) } - } - } - } - } -} - -fun BeanBinder.throttle(): ThrottleCachedBeanBinder { - return ThrottleCachedBeanBinder(this) -} \ No newline at end of file diff --git a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/AbstractStorageTest.kt b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/AbstractStorageTest.kt index 999f33e56..c27139be9 100644 --- a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/AbstractStorageTest.kt +++ b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/AbstractStorageTest.kt @@ -3,11 +3,10 @@ package com.itangcent.idea.plugin.utils import com.google.inject.Inject import com.itangcent.intellij.context.ActionContext import com.itangcent.intellij.extend.guice.with +import com.itangcent.intellij.extend.withBoundary import com.itangcent.mock.AdvancedContextTest import org.junit.jupiter.api.Test -import org.junit.jupiter.api.condition.DisabledOnOs -import org.junit.jupiter.api.condition.OS - +import java.util.* import kotlin.reflect.KClass import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -96,4 +95,50 @@ abstract class AbstractStorageTest : AdvancedContextTest() { } + + @Test + fun crossThread() { + actionContext.withBoundary { + actionContext.runAsync { + storage.set("a", 1) + repeat(50) { + storage.push("share", it * 2) + } + } + actionContext.runAsync { + storage.set("b", 2) + repeat(50) { + storage.push("share", it * 2 + 1) + } + } + } + assertEquals(1, storage.get("a")) + assertEquals(2, storage.get("b")) + val share = storage.get("share") as Collection<*> + assertEquals(100, share.size) + assertEquals((0..99).toSet(), share.toSet()) + assertEquals(setOf("a", "b", "share"), storage.keys().toSet()) + } + + @Test + fun testWrongType() { + //test push element to non-collection + storage.set("a", 1) + assertEquals(1, storage.get("a")) + storage.push("a", 2) + assertEquals(2, storage.peek("a")) + + //test push element to collection(not linked list) + storage.set("b", listOf(1)) + assertEquals(listOf(1), storage.get("b")) + storage.push("b", 2) + assertEquals(listOf(1, 2), storage.get("b")) + + //test push element to linked list + storage.set("c", LinkedList()) + storage.push("c", 1) + assertEquals(1, storage.peek("c")) + storage.push("c", 2) + assertEquals(2, storage.peek("c")) + } } \ No newline at end of file diff --git a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/LocalStorageTest.kt b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/LocalStorageTest.kt index 02c073614..6c9ecb1e6 100644 --- a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/LocalStorageTest.kt +++ b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/LocalStorageTest.kt @@ -1,11 +1,8 @@ package com.itangcent.idea.plugin.utils -import org.junit.jupiter.api.condition.DisabledOnOs -import org.junit.jupiter.api.condition.OS -import kotlin.reflect.KClass - -@DisabledOnOs(OS.WINDOWS) +/** + * Test case of [LocalStorage] + */ class LocalStorageTest : AbstractStorageTest() { - override val storageClass: KClass - get() = LocalStorage::class + override val storageClass = LocalStorage::class } \ No newline at end of file diff --git a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/SessionStorageTest.kt b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/SessionStorageTest.kt index a6a6ec267..9a90f1ca5 100644 --- a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/SessionStorageTest.kt +++ b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/SessionStorageTest.kt @@ -1,8 +1,8 @@ package com.itangcent.idea.plugin.utils -import kotlin.reflect.KClass - +/** + * Test case of [SessionStorage] + */ class SessionStorageTest : AbstractStorageTest() { - override val storageClass: KClass - get() = SessionStorage::class + override val storageClass = SessionStorage::class } \ No newline at end of file diff --git a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/StringDiffHelperTest.kt b/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/StringDiffHelperTest.kt deleted file mode 100644 index 43e238550..000000000 --- a/idea-plugin/src/test/kotlin/com/itangcent/idea/plugin/utils/StringDiffHelperTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.itangcent.idea.plugin.utils - -import com.google.inject.Inject -import com.itangcent.mock.BaseContextTest -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -/** - * Test case of [StringDiffHelper] - */ -internal class StringDiffHelperTest : BaseContextTest() { - - @Inject - private lateinit var stringDiffHelper: StringDiffHelper - - @Test - fun testDiff() { - - assertEquals(0, stringDiffHelper.diff("", "")) - assertEquals(100, stringDiffHelper.diff("", "abcdefg")) - assertEquals(100, stringDiffHelper.diff("abcdefg", "")) - assertEquals(0, stringDiffHelper.diff("abcdefg", "abcdefg")) - assertEquals(100, stringDiffHelper.diff("abcdefg", "1234567")) - - val diff1 = stringDiffHelper.diff("abcdefghijkl", "abcdehijkl") - logger.info("diff of 'abcdefghijkl'&''abcdehijkl' is $diff1") - assertTrue(diff1 > 0) - assertTrue(diff1 < 20) - - val diff2 = stringDiffHelper.diff("abcdefghijkl", "jkl123456") - logger.info("diff of 'abcdefghijkl'&''jkl123456' is $diff2") - assertTrue(diff2 > 50) - assertTrue(diff2 < 80) - assertTrue(diff2 > diff1) - } - - @Test - fun testLargeDiff() { - - assertEquals(100, stringDiffHelper.diff("abcdefghijkl".repeat(100), - "123456789".repeat(100))) - - val diff1 = stringDiffHelper.diff("abcdefghijkl".repeat(100), - "abcdefghijkl".repeat(10) + "123456789".repeat(90)) - assertTrue(diff1 > 80) - assertTrue(diff1 < 100) - } -} \ No newline at end of file