├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ ├── MIT.xml │ └── profiles_settings.xml ├── kotlinc.xml ├── markdown-exported-files.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── modules │ ├── PowerCollections_main.iml │ └── PowerCollections_test.iml └── vcs.xml ├── LICENSE.md ├── PowerCollections.iml ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main └── kotlin │ └── io │ └── bluego │ └── powercollections │ ├── BiMap.kt │ ├── PowerCollections.kt │ ├── Utils.kt │ ├── bounded │ ├── Boundable.kt │ ├── BoundedList.kt │ ├── BoundedMap.kt │ └── Utils.kt │ ├── observable │ ├── list │ │ ├── ListObserver.kt │ │ ├── Observable.kt │ │ ├── ObservableList.kt │ │ └── adapter │ │ │ └── ObservableListAdapter.kt │ └── map │ │ ├── MapObserver.kt │ │ ├── Observable.kt │ │ ├── ObservableMap.kt │ │ └── adapter │ │ └── ObservableMapAdapter.kt │ └── weak │ ├── WeakCollection.kt │ ├── WeakSet.kt │ ├── Weakable.kt │ └── adapter │ └── WeakCollectionAdapter.kt └── test └── kotlin └── io └── bluego └── powercollections ├── BiMapTest.kt ├── PowerCollectionsTest.kt ├── _testutils ├── Utils.kt └── templates │ ├── CollectionTest.kt │ ├── ListTest.kt │ ├── MapTest.kt │ └── SetTest.kt ├── bounded ├── BoundedListTest.kt └── BoundedMapTest.kt ├── observable ├── AbstractDummyObserver.kt ├── list │ ├── DummyListObserver.kt │ └── ObservableListTest.kt └── map │ ├── DummyMapObserver.kt │ └── ObservableMapTest.kt └── weak ├── DummyClass.kt ├── WeakCollectionTest.kt └── WeakSetTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Gradle template 3 | .gradle 4 | /build/ 5 | 6 | # Ignore Gradle GUI config 7 | gradle-app.setting 8 | 9 | # Cache of project 10 | .gradletasknamecache 11 | 12 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 13 | # gradle/wrapper/gradle-wrapper.properties 14 | ### Kotlin template 15 | # Compiled class file 16 | #*.class 17 | 18 | # Log file 19 | *.log 20 | 21 | # BlueJ files 22 | *.ctxt 23 | 24 | # Mobile Tools for Java (J2ME) 25 | .mtj.tmp/ 26 | 27 | # Package Files # 28 | *.jar 29 | *.war 30 | *.ear 31 | *.zip 32 | *.tar.gz 33 | *.rar 34 | 35 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 36 | !gradle-wrapper.jar 37 | 38 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 39 | hs_err_pid* 40 | ### JetBrains template 41 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 42 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 43 | 44 | # User-specific stuff: 45 | .idea/**/workspace.xml 46 | .idea/**/tasks.xml 47 | .idea/dictionaries 48 | 49 | # Sensitive or high-churn files: 50 | .idea/**/dataSources/ 51 | .idea/**/dataSources.ids 52 | .idea/**/dataSources.xml 53 | .idea/**/dataSources.local.xml 54 | .idea/**/sqlDataSources.xml 55 | .idea/**/dynamic.xml 56 | .idea/**/uiDesigner.xml 57 | 58 | # Gradle: 59 | .idea/**/gradle.xml 60 | .idea/**/libraries 61 | 62 | # CMake 63 | cmake-build-debug/ 64 | cmake-build-release/ 65 | 66 | # Mongo Explorer plugin: 67 | .idea/**/mongoSettings.xml 68 | 69 | ## File-based project format: 70 | *.iws 71 | 72 | ## Plugin-specific files: 73 | 74 | # IntelliJ 75 | out/ 76 | 77 | # mpeltonen/sbt-idea plugin 78 | .idea_modules/ 79 | 80 | # JIRA plugin 81 | atlassian-ide-plugin.xml 82 | 83 | # Cursive Clojure plugin 84 | .idea/replstate.xml 85 | 86 | # Crashlytics plugin (for Android Studio and IntelliJ) 87 | com_crashlytics_export_strings.xml 88 | crashlytics.properties 89 | crashlytics-build.properties 90 | fabric.properties 91 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/copyright/MIT.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /.idea/markdown-exported-files.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules/PowerCollections_main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /.idea/modules/PowerCollections_test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Felix Pröhl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /PowerCollections.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | 26 | apply plugin: 'idea' 27 | apply plugin: 'maven' 28 | apply plugin: 'kotlin' 29 | 30 | group = "com.github.Poweranimal" 31 | version = "0.0.1" 32 | 33 | sourceCompatibility = 1.7 // java 7 34 | targetCompatibility = 1.7 35 | 36 | buildscript { 37 | ext.kotlin_version = '1.2.31' 38 | repositories { 39 | jcenter() 40 | } 41 | dependencies { 42 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 43 | 44 | // NOTE: Do not place your application dependencies here; they belong 45 | // in the individual module build.gradle files 46 | } 47 | } 48 | 49 | allprojects { 50 | repositories { 51 | jcenter() 52 | maven { url 'https://jitpack.io' } 53 | } 54 | } 55 | repositories { 56 | mavenCentral() 57 | } 58 | 59 | dependencies { 60 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 61 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 62 | testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" 63 | } 64 | 65 | compileKotlin { 66 | kotlinOptions { 67 | jvmTarget = "1.8" 68 | } 69 | } 70 | compileTestKotlin { 71 | kotlinOptions { 72 | jvmTarget = "1.8" 73 | } 74 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poweranimal/PowerCollections/ef93e98e24857608f14b0eeca5174a14c1867781/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/BiMap.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections 26 | 27 | /** 28 | * This [Map] has unique keys [K] and unique values [V]. 29 | */ 30 | interface BiMap : Map { 31 | 32 | override val values: Set 33 | 34 | /** 35 | * Swaps [keys] and [values]. 36 | * Grants search access to [keys] by [values]. 37 | */ 38 | val inverse: BiMap 39 | } 40 | 41 | /** 42 | * This [MutableMap] has unique [keys] and unique [values]. 43 | */ 44 | interface MutableBiMap : BiMap, MutableMap { 45 | 46 | override val values: MutableSet 47 | 48 | override val inverse: MutableBiMap 49 | 50 | /** 51 | * Removes an already existing pair that contains a value that is identical to the new [value]. 52 | */ 53 | fun forcePut(key: K, value: V): V? 54 | } 55 | 56 | abstract class AbstractBiMap protected constructor( 57 | private val mDirect: MutableMap, 58 | private val mReverse: MutableMap) 59 | : MutableBiMap 60 | { 61 | override val size: Int 62 | get() = mDirect.size 63 | 64 | override val inverse: MutableBiMap by lazy { 65 | object : AbstractBiMap(mReverse, mDirect) { 66 | override val inverse: MutableBiMap 67 | get() = this@AbstractBiMap 68 | } 69 | } 70 | 71 | override val entries: MutableSet> = 72 | BiMapSet(mDirect.entries, { it.key }, { BiMapEntry(it) }) 73 | 74 | override val keys: MutableSet 75 | get() = BiMapSet(mDirect.keys, { it }, { it }) 76 | 77 | override val values: MutableSet 78 | get() = inverse.keys 79 | 80 | 81 | constructor() : this(mutableMapOf(), mutableMapOf()) 82 | 83 | 84 | override fun forcePut(key: K, value: V): V? { 85 | val oldValue = mDirect.put(key, value)?.also { mReverse.remove(it) } 86 | mReverse.put(value, key)?.also { mDirect.remove(it) } 87 | return oldValue 88 | } 89 | 90 | override fun put(key: K, value: V): V? { 91 | require(value !in mReverse) { "BiMap already contains value $value" } 92 | return forcePut(key, value) 93 | } 94 | 95 | override fun putAll(from: Map) { 96 | from.values.forEach { value -> 97 | require(value !in mReverse) { "BiMap already contains value $value" } 98 | } 99 | from.entries.forEach { forcePut(it.key, it.value) } 100 | } 101 | 102 | override fun remove(key: K): V? { 103 | return mDirect.remove(key)?.also { mReverse.remove(it) } 104 | } 105 | 106 | override fun clear() { 107 | mDirect.clear() 108 | mReverse.clear() 109 | } 110 | 111 | override fun get(key: K): V? = mDirect[key] 112 | 113 | override fun containsKey(key: K): Boolean = key in mDirect 114 | 115 | override fun containsValue(value: V): Boolean = value in mReverse 116 | 117 | override fun isEmpty(): Boolean = mDirect.isEmpty() 118 | 119 | private inner class BiMapSet( 120 | private val mElements: MutableSet, 121 | private val mKeyGetter: (T) -> K, 122 | private val mElementWrapper: (T) -> T) 123 | : MutableSet by mElements 124 | { 125 | override fun remove(element: T): Boolean { 126 | if (element !in this) return false 127 | 128 | val key = mKeyGetter(element) 129 | val value = mDirect.remove(key) ?: return false 130 | try { 131 | mReverse.remove(value) 132 | } catch (throwable: Throwable) { 133 | mDirect[key] = value 134 | throw throwable 135 | } 136 | return true 137 | } 138 | 139 | override fun clear() { 140 | mDirect.clear() 141 | mReverse.clear() 142 | } 143 | 144 | override fun iterator(): MutableIterator { 145 | val iterator = mElements.iterator() 146 | return BiMapSetIterator(iterator, mKeyGetter, mElementWrapper) 147 | } 148 | } 149 | 150 | private inner class BiMapSetIterator( 151 | private val mIterator: MutableIterator, 152 | private val mKeyGetter: (T) -> K, 153 | private val mElementWrapper: (T) -> T) 154 | : MutableIterator 155 | { 156 | private var mLast: T? = null 157 | 158 | override fun hasNext(): Boolean { 159 | return mIterator.hasNext() 160 | } 161 | 162 | override fun next(): T { 163 | val element = mIterator.next().apply { 164 | mLast = this 165 | } 166 | return mElementWrapper(element) 167 | } 168 | 169 | override fun remove() { 170 | checkNotNull(mLast as Any?) { "Move to an element before removing it" } 171 | try { 172 | val key = mKeyGetter(mLast!!) 173 | val value = mDirect[key] ?: throw IllegalStateException("BiMap doesn't contain key $key") 174 | mReverse.remove(value) 175 | try { 176 | mIterator.remove() 177 | } catch (throwable: Throwable) { 178 | mReverse[value] = key 179 | throw throwable 180 | } 181 | } finally { 182 | mLast = null 183 | } 184 | } 185 | } 186 | 187 | private inner class BiMapEntry(private val mEntry: MutableMap.MutableEntry) 188 | : MutableMap.MutableEntry by mEntry 189 | { 190 | override fun setValue(newValue: V): V { 191 | if (mEntry.value == newValue) { 192 | mReverse[newValue] = mEntry.key 193 | return try { 194 | mEntry.setValue(newValue) 195 | } catch (throwable: Throwable) { 196 | mReverse[mEntry.value] = mEntry.key 197 | throw throwable 198 | } 199 | } else { 200 | require(newValue !in mReverse) { "BiMap already contains value $newValue" } 201 | mReverse[newValue] = mEntry.key 202 | return try { 203 | mEntry.setValue(newValue) 204 | } catch (throwable: Throwable) { 205 | mReverse.remove(newValue) 206 | throw throwable 207 | } 208 | } 209 | } 210 | } 211 | } 212 | 213 | class HashBiMap(capacity: Int = 16) : AbstractBiMap(HashMap(capacity), HashMap(capacity)) { 214 | companion object { 215 | fun create(map: Map): HashBiMap { 216 | val biMap = HashBiMap() 217 | biMap.putAll(map) 218 | return biMap 219 | } 220 | } 221 | } 222 | 223 | /** 224 | * Creates an empty [MutableBiMap]. 225 | * 226 | * @return Empty [MutableBiMap] 227 | */ 228 | fun mutableBiMapOf(): MutableBiMap = HashBiMap() 229 | 230 | /** 231 | * Creates an [MutableBiMap] with [elements]. 232 | * 233 | * @return [MutableBiMap] with [elements] 234 | */ 235 | fun mutableBiMapOf(vararg elements: Pair): MutableBiMap 236 | = HashBiMap.create(mapOf(*elements)) 237 | 238 | /** 239 | * Creates an [MutableBiMap] with [map]. 240 | * 241 | * @return [MutableBiMap] with [map] 242 | */ 243 | fun mutableBiMapOf(map: Map): MutableBiMap 244 | = HashBiMap.create(map) 245 | 246 | /** 247 | * Creates an empty [BiMap]. 248 | * 249 | * @return Empty [BiMap] 250 | */ 251 | fun biMapOf(): BiMap = mutableBiMapOf() 252 | 253 | /** 254 | * Creates an [BiMap] with [elements]. 255 | * 256 | * @return [BiMap] with [elements] 257 | */ 258 | fun biMapOf(vararg elements: Pair): BiMap 259 | = mutableBiMapOf(*elements) 260 | 261 | /** 262 | * Creates an [BiMap] with [map]. 263 | * 264 | * @return [BiMap] with [map] 265 | */ 266 | fun biMapOf(map: Map): BiMap 267 | = mutableBiMapOf(map) -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/PowerCollections.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections 26 | 27 | import io.bluego.powercollections.bounded.* 28 | import io.bluego.powercollections.observable.list.ListObserverDSL 29 | import io.bluego.powercollections.observable.list.MutableObservableList 30 | import io.bluego.powercollections.observable.list.mutableObservableListOf 31 | import io.bluego.powercollections.observable.map.MapObserverDSL 32 | import io.bluego.powercollections.observable.map.MutableObservableMap 33 | import io.bluego.powercollections.observable.map.mutableObservableMapOf 34 | import io.bluego.powercollections.weak.MutableWeakCollection 35 | import io.bluego.powercollections.weak.MutableWeakSet 36 | import io.bluego.powercollections.weak.mutableWeakCollectionOf 37 | import io.bluego.powercollections.weak.mutableWeakSetOf 38 | import kotlin.properties.ReadWriteProperty 39 | import kotlin.reflect.KProperty 40 | 41 | object PowerCollections { 42 | 43 | inline fun > observableList(noinline observer: ListObserverDSL<*>.() -> Unit): ReadWriteProperty 44 | = object : ReadWriteProperty { 45 | 46 | private var mList: MutableObservableList<*> = mutableObservableListOf(observer) 47 | 48 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 49 | return mList as T 50 | } 51 | 52 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 53 | mList = mutableObservableListOf(observer, *value.toTypedArray()) 54 | } 55 | } 56 | 57 | inline fun > observableMap(noinline observer: MapObserverDSL<*, *>.() -> Unit): ReadWriteProperty 58 | = object : ReadWriteProperty { 59 | 60 | private var mMap: MutableObservableMap<*, *> = mutableObservableMapOf(observer) 61 | 62 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 63 | return mMap as T 64 | } 65 | 66 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 67 | mMap = mutableObservableMapOf(observer, value) 68 | } 69 | } 70 | 71 | inline fun > boundedList(size: Int): ReadWriteProperty 72 | = object : ReadWriteProperty { 73 | 74 | private var mList: MutableBoundedList<*> = mutableBoundedListOf(size) 75 | 76 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 77 | return mList as T 78 | } 79 | 80 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 81 | mList = mutableBoundedListOf(size, *value.toTypedArray()) 82 | } 83 | } 84 | 85 | inline fun > boundedMap(size: Int): ReadWriteProperty 86 | = object : ReadWriteProperty { 87 | 88 | private var mMap: MutableBoundedMap<*, *> = mutableBoundedMapOf(size) 89 | 90 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 91 | return mMap as T 92 | } 93 | 94 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 95 | mMap = mutableBoundedMapOf(size, value) 96 | } 97 | } 98 | 99 | inline fun > weakCollection(): ReadWriteProperty 100 | = object : ReadWriteProperty { 101 | 102 | private var mCollection: MutableWeakCollection<*> = mutableWeakCollectionOf() 103 | 104 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 105 | return mCollection as T 106 | } 107 | 108 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 109 | mCollection = mutableWeakCollectionOf(*value.toTypedArray()) 110 | } 111 | } 112 | 113 | inline fun > weakSet(): ReadWriteProperty 114 | = object : ReadWriteProperty { 115 | 116 | private var mSet: MutableWeakSet<*> = mutableWeakSetOf() 117 | 118 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 119 | return mSet as T 120 | } 121 | 122 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 123 | mSet = mutableWeakSetOf(*value.toTypedArray()) 124 | } 125 | } 126 | 127 | inline fun > biMap(): ReadWriteProperty 128 | = object : ReadWriteProperty { 129 | 130 | private var mMap: MutableBiMap = mutableBiMapOf() 131 | 132 | override fun getValue(thisRef: Any?, property: KProperty<*>): T { 133 | return mMap as T 134 | } 135 | 136 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 137 | mMap = mutableBiMapOf(value) 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/Utils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections 26 | 27 | internal fun Map.getKeyByValue(value: V): K? { 28 | for ((key, value1) in this) { 29 | if (value == value1) return key 30 | } 31 | return null 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/bounded/Boundable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.bounded 26 | 27 | interface Boundable { 28 | 29 | /** 30 | * The max capacity of the implementing list. 31 | * Cannot contain more items than the determined max capacity. 32 | */ 33 | val maxCapacity: Int 34 | } 35 | 36 | interface MutableBoundable : Boundable { 37 | 38 | /** 39 | * Increases or decreases the [maxCapacity]. 40 | */ 41 | fun resize(newSize: Int) 42 | 43 | /** 44 | * Increases or decreases the [maxCapacity]. 45 | * @return true, if item/s got removed 46 | */ 47 | fun forceResize(newSize: Int): Boolean 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/bounded/BoundedMap.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.bounded 26 | 27 | import io.bluego.powercollections.bounded.Utils.checkInitialMaxCapacity 28 | import io.bluego.powercollections.bounded.Utils.checkResize 29 | 30 | /** 31 | * A [Map] that has a fixed capacity of [Map.entries]. 32 | */ 33 | interface BoundedMap: Map, Boundable 34 | 35 | /** 36 | * A [MutableList] that has a fixed capacity of [Map.entries]. 37 | */ 38 | interface MutableBoundedMap: MutableMap, BoundedMap, MutableBoundable { 39 | 40 | /** 41 | * Puts the entry in the [MutableBoundedMap]. 42 | * @throws IndexOutOfBoundsException, if the [MutableMap]'s [maxCapacity] is reached. 43 | */ 44 | override fun put(key: K, value: V): V? 45 | 46 | /** 47 | * Puts the entries in the [MutableBoundedMap]. 48 | * @throws IndexOutOfBoundsException, if the [MutableMap]'s [maxCapacity] is reached. 49 | */ 50 | override fun putAll(from: Map) 51 | 52 | /** 53 | * Adds items to the [MutableBoundedMap]. 54 | * If the [MutableBoundedMap]'s [maxCapacity] is reached, the item gets added to the tail 55 | * and the head (the oldest item) gets removed. 56 | * @return true, if head item was removed 57 | */ 58 | fun forcePut(key: K, value: V): Boolean 59 | 60 | /** 61 | * Adds items to the [MutableBoundedMap]. 62 | * If the [MutableBoundedMap]'s [maxCapacity] is reached, the item gets added to the tail 63 | * and the head (the oldest item) gets removed. 64 | * @return true, if head item was removed 65 | */ 66 | fun forcePutAll(from: Map): Boolean 67 | } 68 | 69 | /** 70 | * A [MutableBoundedMap] baked by [LinkedHashMap] that has a fixed capacity determined by [maxCapacity]. 71 | */ 72 | abstract class AbstractBoundedMap 73 | : LinkedHashMap, MutableBoundedMap 74 | { 75 | 76 | constructor(maxCapacity: Int) : super() { 77 | checkInitialMaxCapacity(maxCapacity) 78 | this.maxCapacity = maxCapacity 79 | } 80 | 81 | constructor(maxCapacity: Int, initialCapacity: Int, loadFactor: Float) 82 | : super(initialCapacity, loadFactor) { 83 | checkInitialMaxCapacity(maxCapacity) 84 | this.maxCapacity = maxCapacity 85 | } 86 | 87 | constructor(maxCapacity: Int, initialCapacity: Int) 88 | : super(initialCapacity) { 89 | checkInitialMaxCapacity(maxCapacity) 90 | this.maxCapacity = maxCapacity 91 | } 92 | 93 | constructor(maxCapacity: Int, m: Map?) 94 | : super(m) { 95 | checkInitialMaxCapacity(maxCapacity) 96 | this.maxCapacity = maxCapacity 97 | } 98 | 99 | constructor(maxCapacity: Int, initialCapacity: Int, loadFactor: Float, accessOrder: Boolean) 100 | : super(initialCapacity, loadFactor, accessOrder) { 101 | checkInitialMaxCapacity(maxCapacity) 102 | this.maxCapacity = maxCapacity 103 | } 104 | 105 | /** 106 | * The max capacity of the [BoundedMap]. 107 | * Cannot contain more entries than [maxCapacity]. 108 | */ 109 | final override var maxCapacity: Int 110 | private set 111 | 112 | private var mEldestEntryRemoved = false 113 | private var mIsRunningSensitive = false 114 | 115 | override val entries: MutableSet> 116 | get() = super.entries 117 | 118 | override val keys: MutableSet 119 | get() = super.keys 120 | 121 | override val values: MutableCollection 122 | get() = super.values 123 | 124 | override fun removeEldestEntry(p0: MutableMap.MutableEntry?): Boolean { 125 | val result = size > maxCapacity 126 | if (result) { 127 | mEldestEntryRemoved = true 128 | if (mIsRunningSensitive) { 129 | remove(entries.last().key) ?: throw NoSuchElementException() 130 | mEldestEntryRemoved = false 131 | throw IndexOutOfBoundsException( 132 | "The BoundedMap's maxCapacity of $maxCapacity is already reached") 133 | } 134 | } 135 | return result 136 | } 137 | 138 | override fun put(key: K, value: V): V? = runSensitive { super.put(key, value) } 139 | 140 | override fun putAll(from: Map) = runSensitive { super.putAll(from) } 141 | 142 | override fun putIfAbsent(key: K, value: V): V? 143 | = runSensitive { super.putIfAbsent(key, value) } 144 | 145 | @Suppress("ReplacePutWithAssignment") 146 | override fun forcePut(key: K, value: V): Boolean 147 | = checkEldestEntryRemoved { super.put(key, value) } 148 | 149 | override fun forcePutAll(from: Map): Boolean 150 | = checkEldestEntryRemoved { super.putAll(from) } 151 | 152 | override fun forceResize(newSize: Int): Boolean { 153 | maxCapacity = newSize 154 | return if (newSize < size) { 155 | this.iterator().removeAll(size - newSize) 156 | true 157 | } else false 158 | } 159 | 160 | override fun resize(newSize: Int) { 161 | checkResize(size, newSize) 162 | maxCapacity = newSize 163 | } 164 | 165 | private inline fun runSensitive(action: () -> E) : E { 166 | mIsRunningSensitive = true 167 | val result: E = action() 168 | mIsRunningSensitive = false 169 | return result 170 | } 171 | 172 | private inline fun checkEldestEntryRemoved(action: () -> Unit) : Boolean { 173 | action() 174 | val result = mEldestEntryRemoved 175 | mEldestEntryRemoved = false 176 | return result 177 | } 178 | 179 | /** 180 | * Remove [n] elements from [MutableIterator]. 181 | * Starts removing from the first [Iterator.next] element. 182 | */ 183 | private fun MutableIterator.removeAll(n: Int) { 184 | check(n > 0) { "n must be greater than 0" } 185 | var counter = 0 186 | do { 187 | if (!hasNext()) throw IndexOutOfBoundsException("ERROR: n (= $n) is greater than the " + 188 | "iterators max size (= $counter)!") 189 | if (counter >= n) return 190 | next() 191 | remove() 192 | counter++ 193 | } while (true) 194 | } 195 | } 196 | 197 | internal class BoundedMapBuilder : AbstractBoundedMap { 198 | 199 | private constructor(maxCapacity: Int) : super(maxCapacity) 200 | private constructor(maxCapacity: Int, initialCapacity: Int, loadFactor: Float) 201 | : super(maxCapacity, initialCapacity, loadFactor) 202 | private constructor(maxCapacity: Int, initialCapacity: Int) 203 | : super(maxCapacity, initialCapacity) 204 | private constructor(maxCapacity: Int, m: Map?) : super(maxCapacity, m) 205 | private constructor(maxCapacity: Int, initialCapacity: Int, loadFactor: Float, 206 | accessOrder: Boolean) 207 | : super(maxCapacity, initialCapacity, loadFactor, accessOrder) 208 | 209 | companion object { 210 | 211 | fun create(maxCapacity: Int) = BoundedMapBuilder(maxCapacity) 212 | 213 | fun create(maxCapacity: Int, initialCapacity: Int) 214 | = BoundedMapBuilder(maxCapacity, initialCapacity) 215 | 216 | fun create(maxCapacity: Int, initialCapacity: Int, loadFactor: Float) 217 | = BoundedMapBuilder(maxCapacity, initialCapacity, loadFactor) 218 | 219 | fun create(maxCapacity: Int, initialCapacity: Int, loadFactor: Float, 220 | accessOrder: Boolean) 221 | = BoundedMapBuilder(maxCapacity, initialCapacity, loadFactor, accessOrder) 222 | 223 | fun create(maxCapacity: Int, map: Map) = BoundedMapBuilder(maxCapacity, map) 224 | 225 | } 226 | } 227 | 228 | /** 229 | * Creates an empty [MutableBoundedMap]. 230 | * 231 | * @return Empty [MutableBoundedMap] 232 | */ 233 | fun mutableBoundedMapOf(maxCapacity: Int): MutableBoundedMap 234 | = BoundedMapBuilder.create(maxCapacity) 235 | 236 | /** 237 | * Creates an [MutableBoundedMap] with [elements]. 238 | * 239 | * @return [MutableBoundedMap] with [elements] 240 | */ 241 | fun mutableBoundedMapOf(maxCapacity: Int, vararg elements: Pair): MutableBoundedMap = 242 | BoundedMapBuilder.create(maxCapacity, mapOf(*elements)) 243 | 244 | /** 245 | * Creates an [MutableBoundedMap] with [map]. 246 | * 247 | * @return [MutableBoundedMap] with [map] 248 | */ 249 | fun mutableBoundedMapOf(maxCapacity: Int, map: Map): MutableBoundedMap = 250 | BoundedMapBuilder.create(maxCapacity, map) 251 | 252 | /** 253 | * Creates an empty [BoundedMap]. 254 | * 255 | * @return Empty [BoundedMap] 256 | */ 257 | fun boundedMapOf(maxCapacity: Int): BoundedMap = mutableBoundedMapOf(maxCapacity) 258 | 259 | /** 260 | * Creates an [BoundedMap] with [elements]. 261 | * 262 | * @return [BoundedMap] with [elements] 263 | */ 264 | fun boundedMapOf(maxCapacity: Int, vararg elements: Pair): BoundedMap = 265 | mutableBoundedMapOf(maxCapacity, *elements) 266 | 267 | /** 268 | * Creates an [BoundedMap] with [map]. 269 | * 270 | * @return [BoundedMap] with [map] 271 | */ 272 | fun boundedMapOf(maxCapacity: Int, map: Map): BoundedMap = 273 | mutableBoundedMapOf(maxCapacity, map) -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/bounded/Utils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.bounded 26 | 27 | internal object Utils { 28 | 29 | fun checkResize(currentSize: Int, newSize: Int) { 30 | if (newSize < currentSize) throw IllegalStateException("Resizing would lose data") 31 | } 32 | 33 | fun checkInitialMaxCapacity(initialMaxCapacity: Int) { 34 | if (initialMaxCapacity <= 0) throw IndexOutOfBoundsException( 35 | "The minimum maxCapacity must be greater than 0") 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/list/ListObserver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.list 26 | 27 | interface ListObserver { 28 | 29 | /** 30 | * Manually called, if changes has been made 31 | */ 32 | fun notifyDataChanged() 33 | 34 | /** 35 | * Manually called, if changes has been made at [index]. 36 | */ 37 | fun notifyDataChanged(index: Int) 38 | 39 | /** 40 | * Called when a new item was added. 41 | * Actions that may trigger this event: 42 | */ 43 | fun wasAdded(index: Int, element: E) 44 | 45 | /** 46 | * Called when new items were added. 47 | */ 48 | fun wasAdded(elements: Map) 49 | 50 | /** 51 | * Called when items were removed. 52 | */ 53 | fun wasRemoved(index: Int, element: E) 54 | 55 | /** 56 | * Called when items were removed. 57 | */ 58 | fun wasRemoved(elements: Map) 59 | 60 | /** 61 | * Called when a item was replaced by a new item. 62 | */ 63 | fun wasReplaced(index: Int, lastElement: E, newElement: E) 64 | 65 | /** 66 | * Called when a item was replaced by a new item. 67 | */ 68 | fun wasReplaced(map: Map>) 69 | } 70 | 71 | @Suppress("AddVarianceModifier") 72 | class ListObserverDSL private constructor() { 73 | 74 | companion object { 75 | 76 | private const val ERROR_MSG = "Set safetyMode to false in order to disable this error" 77 | 78 | fun create(listObserverDSL: ListObserverDSL.() -> Unit): ListObserver { 79 | return ListObserverDSL().apply(listObserverDSL).run { 80 | object : ListObserver { 81 | override fun notifyDataChanged() = mNotifyDataChanged() 82 | 83 | override fun notifyDataChanged(index: Int) = mNotifyDataChangedIndex(index) 84 | 85 | override fun wasAdded(index: Int, element: E) = mWasAdded(index, element) 86 | 87 | override fun wasAdded(elements: Map) = mWasAddedMultiple(elements) 88 | 89 | override fun wasRemoved(index: Int, element: E) = mWasRemoved(index, element) 90 | 91 | override fun wasRemoved(elements: Map) = mWasRemovedMultiple(elements) 92 | 93 | override fun wasReplaced(index: Int, lastElement: E, newElement: E) 94 | = mWasReplaced(index, lastElement, newElement) 95 | 96 | override fun wasReplaced(map: Map>) = mWasReplacedMultiple(map) 97 | 98 | } 99 | } 100 | } 101 | } 102 | 103 | private var mNotifyDataChanged: () -> Unit = { default() } 104 | private var mNotifyDataChangedIndex: (Int) -> Unit = { default() } 105 | private var mWasAdded: (Int, E) -> Unit = { _, _ -> } 106 | private var mWasAddedMultiple: (Map) -> Unit = { default() } 107 | private var mWasRemoved: (Int, E) -> Unit = { _, _ -> default() } 108 | private var mWasRemovedMultiple: (Map) -> Unit = { default() } 109 | private var mWasReplaced: (Int, E, E) -> Unit = { _, _, _ -> default() } 110 | private var mWasReplacedMultiple: (Map>) -> Unit = { default() } 111 | 112 | private fun default() { 113 | if (safetyMode) throw NotImplementedError(ERROR_MSG) 114 | } 115 | 116 | /** 117 | * If true, all methods of [ListObserver] that has not been added by [ListObserverDSL] will throw [NotImplementedError]. 118 | */ 119 | var safetyMode: Boolean = true 120 | 121 | /** 122 | * @see [ListObserver.notifyDataChanged] 123 | */ 124 | fun notifyDataChanged(action: () -> Unit) { 125 | mNotifyDataChanged = action 126 | } 127 | 128 | /** 129 | * @see [ListObserver.notifyDataChanged] 130 | */ 131 | fun notifyDataChangedByIndex(action: (index: Int) -> Unit) { 132 | mNotifyDataChangedIndex = action 133 | } 134 | 135 | /** 136 | * @see [ListObserver.wasAdded] 137 | */ 138 | fun wasAdded(action: (index: Int, element: E) -> Unit) { 139 | mWasAdded = action 140 | } 141 | 142 | /** 143 | * @see [ListObserver.wasAdded] 144 | */ 145 | fun wasAdded(action: (elements: Map) -> Unit) { 146 | mWasAddedMultiple = action 147 | } 148 | 149 | /** 150 | * @see [ListObserver.wasRemoved] 151 | */ 152 | fun wasRemoved(action: (index: Int, element: E) -> Unit) { 153 | mWasRemoved = action 154 | } 155 | 156 | /** 157 | * @see [ListObserver.wasRemoved] 158 | */ 159 | fun wasRemoved(action: (Map) -> Unit) { 160 | mWasRemovedMultiple = action 161 | } 162 | 163 | /** 164 | * @see [ListObserver.wasReplaced] 165 | */ 166 | fun wasReplaced(action: (index: Int, lastElement: E, newElement: E) -> Unit) { 167 | mWasReplaced = action 168 | } 169 | 170 | /** 171 | * @see [ListObserver.wasReplaced] 172 | */ 173 | fun wasReplaced(action: (Map>) -> Unit) { 174 | mWasReplacedMultiple = action 175 | } 176 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/list/Observable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.list 26 | 27 | interface Observable { 28 | 29 | val observer: ListObserver 30 | 31 | fun notifyDataChanged() 32 | 33 | fun notifyDataChanged(index: Int) 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/list/ObservableList.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.list 26 | 27 | import io.bluego.powercollections.observable.list.adapter.ObservableListAdapter 28 | import java.util.function.Predicate 29 | import java.util.function.UnaryOperator 30 | 31 | interface ObservableList: List 32 | 33 | interface MutableObservableList: ObservableList, MutableList, Observable { 34 | 35 | fun runSilentChanges(silentChanges: MutableList.() -> Unit) 36 | 37 | } 38 | 39 | abstract class AbstractObservableList: ArrayList, MutableObservableList 40 | { 41 | private val mAdapter: ObservableListAdapter, E> by lazy { 42 | ObservableListAdapter(observer, this) 43 | } 44 | 45 | constructor(initialCapacity: Int, observer: ListObserver) : super(initialCapacity) 46 | { 47 | this.observer = observer 48 | } 49 | 50 | constructor(observer: ListObserver) : super() { 51 | this.observer = observer 52 | } 53 | 54 | constructor(c: Collection?, observer: ListObserver) : super(c) 55 | { 56 | this.observer = observer 57 | } 58 | 59 | final override var observer: ListObserver 60 | private set 61 | 62 | override fun notifyDataChanged() = mAdapter.notifyDataChanged() 63 | 64 | override fun notifyDataChanged(index: Int) = mAdapter.notifyDataChanged(index) 65 | 66 | override fun runSilentChanges(silentChanges: MutableList.() -> Unit) = mAdapter.runSilentChanges(silentChanges) 67 | 68 | override fun add(element: E): Boolean = mAdapter.add(element) { e -> super.add(e) } 69 | 70 | override fun add(index: Int, element: E) = mAdapter.add(index, element) { i, e -> super.add(i, e) } 71 | 72 | override fun addAll(index: Int, elements: Collection): Boolean 73 | = mAdapter.addAll(index, elements) { i, collection -> super.addAll(i, collection) } 74 | 75 | override fun iterator(): MutableIterator = super.iterator() 76 | 77 | override fun listIterator(): MutableListIterator = super.listIterator() 78 | 79 | override fun listIterator(index: Int): MutableListIterator = super.listIterator(index) 80 | 81 | override fun subList(fromIndex: Int, toIndex: Int): MutableList 82 | = mAdapter.subList(fromIndex, toIndex) { fi, ti -> super.subList(fi, ti) } 83 | 84 | override fun addAll(elements: Collection): Boolean = 85 | mAdapter.addAll(elements) { collection -> super.addAll(collection) } 86 | 87 | override fun clear() = mAdapter.clear { super.clear() } 88 | 89 | override fun remove(element: E): Boolean = mAdapter.remove(element) { e -> super.remove(e) } 90 | 91 | override fun removeAll(elements: Collection): Boolean 92 | = mAdapter.removeAll(elements) { collection -> super.removeAll(collection) } 93 | 94 | override fun removeAt(index: Int): E = mAdapter.removeAt(index) { i -> super.removeAt(i) } 95 | 96 | override fun set(index: Int, element: E): E = mAdapter.set(index, element) { i, e -> super.set(i, e) } 97 | 98 | override fun replaceAll(p0: UnaryOperator) = mAdapter.replaceAll(p0) { super.replaceAll(p0) } 99 | 100 | override fun retainAll(elements: Collection): Boolean = mAdapter.retainAll(elements, this::listIterator) 101 | 102 | override fun removeIf(p0: Predicate): Boolean 103 | = mAdapter.removeIf(p0) { predicate -> super< ArrayList>.removeIf(predicate) } 104 | 105 | override fun removeRange(fromIndex: Int, toIndex: Int) 106 | = mAdapter.removeRange(fromIndex, toIndex) { fi, ti -> super.removeRange(fi, ti) } 107 | 108 | } 109 | 110 | class ArrayObservableList : AbstractObservableList 111 | { 112 | constructor(initialCapacity: Int, mObserver: ListObserver) : super(initialCapacity, mObserver) 113 | constructor(mObserver: ListObserver) : super(mObserver) 114 | constructor(c: Collection?, mObserver: ListObserver) : super(c, mObserver) 115 | } 116 | 117 | 118 | /** 119 | * Creates an empty [MutableObservableList]. 120 | * 121 | * @return Empty [MutableObservableList] 122 | */ 123 | fun mutableObservableListOf(observer: ListObserver) 124 | : MutableObservableList = ArrayObservableList(observer) 125 | 126 | /** 127 | * @see ListObserverDSL 128 | * @see mutableObservableListOf 129 | */ 130 | fun mutableObservableListOf(observer: ListObserverDSL.() -> Unit) : MutableObservableList 131 | = mutableObservableListOf(ListObserverDSL.create(observer)) 132 | 133 | /** 134 | * Creates an [MutableObservableList] with [elements]. 135 | * 136 | * @return [MutableObservableList] with [elements] 137 | */ 138 | fun mutableObservableListOf( 139 | observer: ListObserver, 140 | vararg elements: E): MutableObservableList 141 | = ArrayObservableList(listOf(*elements), observer) 142 | 143 | /** 144 | * @see ListObserverDSL 145 | * @see mutableObservableListOf 146 | */ 147 | fun mutableObservableListOf( 148 | observer: ListObserverDSL.() -> Unit, 149 | vararg elements: E) : MutableObservableList 150 | = mutableObservableListOf(ListObserverDSL.create(observer), *elements) 151 | 152 | /** 153 | * Creates an empty [ObservableList]. 154 | * 155 | * @return Empty [ObservableList] 156 | */ 157 | fun observableListOf( 158 | observer: ListObserver) : ObservableList 159 | = mutableObservableListOf(observer) 160 | 161 | /** 162 | * @see ListObserverDSL 163 | * @see observableListOf 164 | */ 165 | fun observableListOf(observer: ListObserverDSL.() -> Unit) : ObservableList 166 | = mutableObservableListOf(observer) 167 | 168 | /** 169 | * Creates an [ObservableList] with [elements]. 170 | * 171 | * @return [ObservableList] with [elements] 172 | */ 173 | fun observableListOf(observer: ListObserver, vararg elements: E) : ObservableList 174 | = mutableObservableListOf(observer, *elements) 175 | 176 | /** 177 | * @see ListObserverDSL 178 | * @see observableListOf 179 | */ 180 | fun observableListOf(observer: ListObserverDSL.() -> Unit, vararg elements: E): ObservableList 181 | = mutableObservableListOf(observer, *elements) -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/list/adapter/ObservableListAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.list.adapter 26 | 27 | import io.bluego.powercollections.observable.list.ListObserver 28 | import java.util.function.Predicate 29 | import java.util.function.UnaryOperator 30 | import kotlin.properties.Delegates 31 | 32 | internal open class ObservableListAdapter, E>(private val mObserver: ListObserver, 33 | private val mList: T) 34 | { 35 | protected var isSilent = false 36 | 37 | open fun notifyDataChanged() = mObserver.notifyDataChanged() 38 | 39 | open fun notifyDataChanged(index: Int) = mObserver.notifyDataChanged(index) 40 | 41 | open fun runSilentChanges(silentChanges: T.() -> Unit) { 42 | isSilent = true 43 | silentChanges(mList) 44 | isSilent = false 45 | } 46 | 47 | open fun add(element: E, add: (E) -> Boolean): Boolean { 48 | return add(element).apply { if (!isSilent) mObserver.wasAdded(mList.lastIndex, element) } 49 | } 50 | 51 | open fun add(index: Int, element: E, add: (Int, E) -> Unit) { 52 | add(index, element).also { if (!isSilent) mObserver.wasAdded(mList.lastIndex, element) } 53 | } 54 | 55 | open fun addAll(index: Int, elements: Collection, addAll: (Int, Collection) -> Boolean) 56 | : Boolean 57 | { 58 | val startIndex = mList.size 59 | return addAll(index, elements).also { 60 | if (!isSilent && it) mObserver.wasAdded( 61 | elements.mapIndexed { index, e -> startIndex + index to e }.toMap()) 62 | } 63 | } 64 | 65 | open fun addAll(elements: Collection, addAll: (Collection) -> Boolean): Boolean { 66 | val startIndex = mList.size 67 | return addAll(elements).also { 68 | if (!isSilent && it) mObserver.wasAdded( 69 | elements.mapIndexed { index, e -> startIndex + index to e }.toMap()) 70 | } 71 | } 72 | 73 | open fun clear(clear: () -> Unit) { 74 | if (!isSilent) { 75 | val list = mList.mapIndexed { index, e -> Pair(index, e) }.toMap() 76 | clear() 77 | mObserver.wasRemoved(list) 78 | } else clear() 79 | } 80 | 81 | open fun remove(element: E, remove: (E) -> Boolean): Boolean { 82 | return if (!isSilent) { 83 | val index = mList.indexOf(element) 84 | return if (index != -1) { 85 | val result = remove(element) 86 | assert(result) { "Result cannot be false. There must be a bug..." } 87 | mObserver.wasRemoved(index, element) 88 | result 89 | } else false 90 | } else remove(element) 91 | } 92 | 93 | open fun removeIf(p0: Predicate, removeIf: (Predicate) -> Boolean): Boolean { 94 | throw NotImplementedError() 95 | } 96 | 97 | open fun removeAt(index: Int, removeAt: (Int) -> E): E { 98 | return removeAt(index).also { if (!isSilent) mObserver.wasRemoved(index, it) } 99 | } 100 | 101 | open fun removeAll(elements: Collection, removeAll: (Collection) -> Boolean): Boolean { 102 | return if (!isSilent) { 103 | val toBeRemoved = elements.mapNotNull { 104 | val index = mList.indexOf(it) 105 | if (index > -1) Pair(index, it) 106 | else null 107 | }.toMap() 108 | if (toBeRemoved.isNotEmpty()) { 109 | val result = removeAll(elements) 110 | mObserver.wasRemoved(toBeRemoved) 111 | assert(result) { "Result must not be false. There must be a bug..." } 112 | result 113 | } else false 114 | } else removeAll(elements) 115 | } 116 | 117 | open fun removeRange(fromIndex: Int, toIndex: Int, removeRange: (Int, Int) -> Unit) { 118 | val rng = 0 .. mList.size 119 | var removedRange by Delegates.notNull>() 120 | if (fromIndex in rng && toIndex in rng) { 121 | removedRange = mList.subList(fromIndex, toIndex).mapIndexed { index, e -> index to e }.toMap() 122 | } 123 | removeRange(fromIndex, toIndex) 124 | mObserver.wasRemoved(removedRange) 125 | } 126 | 127 | open fun replaceAll(p0: UnaryOperator, replaceAll: (UnaryOperator) -> Unit) { 128 | throw NotImplementedError() 129 | } 130 | 131 | open fun retainAll(elements: Collection, retainAll: () -> MutableListIterator): Boolean { 132 | requireNotNull(elements) 133 | var modified = false 134 | val it = retainAll() 135 | while (it.hasNext()) { 136 | if (!elements.contains(it.next())) { 137 | it.remove() 138 | modified = true 139 | } 140 | } 141 | return modified 142 | } 143 | 144 | open fun set(index: Int, element: E, set: (Int, E) -> E): E { 145 | return if (!isSilent) { 146 | val result = set(index, element) 147 | mObserver.wasReplaced(index, result, element) 148 | result 149 | } else set(index, element) 150 | } 151 | 152 | open fun subList(fromIndex: Int, toIndex: Int, subList: (Int, Int) -> MutableList): MutableList { 153 | return subList(fromIndex, toIndex) 154 | } 155 | 156 | open fun iterator(iterator: () -> MutableIterator): MutableIterator { 157 | return MutableObservableIterator(iterator()) 158 | } 159 | 160 | open fun listIterator(listIterator: () -> MutableListIterator): MutableListIterator { 161 | return MutableObservableListIterator(listIterator()) 162 | } 163 | 164 | open fun listIterator(index: Int, listIterator: (Int) -> MutableListIterator): MutableListIterator { 165 | return MutableObservableListIterator(listIterator(index)) 166 | } 167 | 168 | protected open inner class MutableObservableIterator( 169 | private val mIterator: MutableIterator) 170 | :MutableIterator by mIterator 171 | { 172 | protected var last: E? = null 173 | 174 | override fun hasNext(): Boolean = mIterator.hasNext() 175 | 176 | override fun next(): E = mIterator.next().apply { last = this } 177 | 178 | //TODO: write more efficient index tracker 179 | @Suppress("UNCHECKED_CAST") 180 | override fun remove() { 181 | if (!isSilent) { 182 | val index = mList.indexOf(last) 183 | check(index >= 0) { "Index must not be smaller than 0. There must be a bug." } 184 | try { 185 | mIterator.remove() 186 | mObserver.wasRemoved(index, last as E) 187 | } finally { 188 | last = null 189 | } 190 | } else { 191 | try { 192 | mIterator.remove() 193 | } finally { 194 | last = null 195 | } 196 | } 197 | } 198 | } 199 | 200 | protected open inner class MutableObservableListIterator( 201 | private val mIterator: MutableListIterator) 202 | : MutableObservableIterator(mIterator), MutableListIterator 203 | { 204 | @Suppress("UNCHECKED_CAST") 205 | override fun remove() { 206 | if (!isSilent) { 207 | val index = nextIndex() - 1 208 | try { 209 | mIterator.remove() 210 | mObserver.wasRemoved(index, last as E) 211 | } finally { 212 | last = null 213 | } 214 | } else { 215 | try { 216 | mIterator.remove() 217 | } finally { 218 | last = null 219 | } 220 | } 221 | } 222 | 223 | override fun previous(): E = mIterator.previous().apply { last = this } 224 | 225 | override fun add(element: E) { 226 | if (!isSilent) { 227 | val index = nextIndex() 228 | mIterator.add(element) 229 | mObserver.wasAdded(index, element) 230 | } else mIterator.add(element) 231 | } 232 | 233 | @Suppress("UNCHECKED_CAST") 234 | override fun set(element: E) { 235 | if (!isSilent) { 236 | val index = nextIndex() - 1 237 | mIterator.set(element) 238 | mObserver.wasReplaced(index, last as E, element) 239 | } else mIterator.set(element) 240 | } 241 | 242 | override fun hasPrevious(): Boolean = mIterator.hasPrevious() 243 | 244 | override fun nextIndex(): Int = mIterator.nextIndex() 245 | 246 | override fun previousIndex(): Int = mIterator.previousIndex() 247 | } 248 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/map/MapObserver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.map 26 | 27 | import io.bluego.powercollections.observable.list.ListObserver 28 | import io.bluego.powercollections.observable.list.ListObserverDSL 29 | 30 | 31 | interface MapObserver { 32 | 33 | /** 34 | * Manually called, if changes in an [Map.Entry.value] has been made. 35 | */ 36 | fun notifyDataChanged() 37 | 38 | /** 39 | * Manually called, if changes in a class in an [Map.Entry.value] has been made. 40 | * Specifies the class that has been changed by its [key]. 41 | * 42 | * @param key of the item's content that has been changed 43 | */ 44 | fun notifyDataChanged(key: K) 45 | 46 | /** 47 | * Called when new values are added. 48 | */ 49 | fun wasAdded(key: K, value: V) 50 | 51 | /** 52 | * Called when new values are added. 53 | */ 54 | fun wasAdded(entries: Map) 55 | 56 | /** 57 | * Called when values are removed. 58 | */ 59 | fun wasRemoved(key: K, value: V) 60 | 61 | /** 62 | * Called when values are removed. 63 | */ 64 | fun wasRemoved(entries: Map) 65 | 66 | /** 67 | * Called when a value is replaced with a new value. 68 | */ 69 | fun wasReplaced(key: K, lastValue: V, newValue: V) 70 | 71 | /** 72 | * Called when a value is replaced with a new value. 73 | * 'Pair.first = oldValue; Pair.second = newValue' 74 | */ 75 | fun wasReplaced(entries: Map>) 76 | } 77 | 78 | @Suppress("AddVarianceModifier") 79 | class MapObserverDSL private constructor() { 80 | 81 | companion object { 82 | 83 | private const val ERROR_MSG = "Set safetyMode to false in order to disable this error" 84 | 85 | fun create(mapObserver: MapObserverDSL.() -> Unit): MapObserver { 86 | 87 | return MapObserverDSL().apply(mapObserver).run { 88 | 89 | object : MapObserver { 90 | 91 | override fun notifyDataChanged() = mNotifyDataChanged() 92 | 93 | override fun notifyDataChanged(key: K) = mNotifyDataChangedKey(key) 94 | 95 | override fun wasAdded(key: K, value: V) = mWasAdded(key, value) 96 | 97 | override fun wasAdded(entries: Map) = mWasAddedMultiple(entries) 98 | 99 | override fun wasRemoved(key: K, value: V) = mWasRemoved(key, value) 100 | 101 | override fun wasRemoved(entries: Map) = mWasRemovedMultiple(entries) 102 | 103 | override fun wasReplaced(key: K, lastValue: V, newValue: V) = mWasReplaced(key, lastValue, newValue) 104 | 105 | override fun wasReplaced(entries: Map>) = mWasReplacedMultiple(entries) 106 | 107 | } 108 | } 109 | } 110 | } 111 | 112 | private var mNotifyDataChanged: () -> Unit = { default() } 113 | private var mNotifyDataChangedKey: (K) -> Unit = { default() } 114 | private var mWasAdded: (K, V) -> Unit = { _, _ -> default() } 115 | private var mWasAddedMultiple: (Map) -> Unit = { default() } 116 | private var mWasRemoved: (K, V) -> Unit = { _, _ -> default() } 117 | private var mWasRemovedMultiple: (Map) -> Unit = { default() } 118 | private var mWasReplaced: (K, V, V) -> Unit = { _, _, _ -> default() } 119 | private var mWasReplacedMultiple: (Map>) -> Unit = { default() } 120 | 121 | private fun default() { 122 | if (safetyMode) throw NotImplementedError(ERROR_MSG) 123 | } 124 | 125 | /** 126 | * If true, all methods of [ListObserver] that has not been added by [ListObserverDSL] will throw [NotImplementedError]. 127 | */ 128 | var safetyMode: Boolean = true 129 | 130 | /** 131 | * @see [MapObserver.notifyDataChanged] 132 | */ 133 | fun notifyDataChanged(action: () -> Unit) { 134 | mNotifyDataChanged = action 135 | } 136 | 137 | /** 138 | * @see [MapObserver.notifyDataChanged] 139 | */ 140 | fun notifyDataChangedByKey(action: (key: K) -> Unit) { 141 | mNotifyDataChangedKey = action 142 | } 143 | 144 | /** 145 | * @see [MapObserver.wasAdded] 146 | */ 147 | fun addedSingleItem(action: (key: K, value: V) -> Unit) { 148 | mWasAdded = action 149 | } 150 | 151 | /** 152 | * @see [MapObserver.wasAdded] 153 | */ 154 | fun addedMultipleItems(action: (entries: Map) -> Unit) { 155 | mWasAddedMultiple = action 156 | } 157 | 158 | /** 159 | * @see [MapObserver.wasRemoved] 160 | */ 161 | fun removedSingleItem(action: (key: K, value: V) -> Unit) { 162 | mWasRemoved = action 163 | } 164 | 165 | /** 166 | * @see [MapObserver.wasRemoved] 167 | */ 168 | fun removedMultipleItems(action: (Map) -> Unit) { 169 | mWasRemovedMultiple = action 170 | } 171 | 172 | /** 173 | * @see [MapObserver.wasReplaced] 174 | */ 175 | fun replacedSingleItem(action: (key: K, lastValue: V, newValue: V) -> Unit) { 176 | mWasReplaced = action 177 | } 178 | 179 | /** 180 | * @see [MapObserver.wasReplaced] 181 | */ 182 | fun replacedMultipleItems(action: (Map>) -> Unit) { 183 | mWasReplacedMultiple = action 184 | } 185 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/map/Observable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.map 26 | 27 | interface Observable { 28 | 29 | val observer: MapObserver 30 | 31 | fun notifyDataChanged() 32 | 33 | fun notifyDataChanged(key: K) 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/map/ObservableMap.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.map 26 | 27 | import io.bluego.powercollections.observable.map.adapter.ObservableMapAdapter 28 | import java.util.function.BiFunction 29 | 30 | interface ObservableMap: Map 31 | 32 | /** 33 | * This [MutableMap] keeps track of adding, removing and replacing items in this map. 34 | * The following actions are tracked by the [MapObserver]: 35 | * * [MutableObservableMap.put] 36 | * * [MutableObservableMap.putIfAbsent] 37 | * * [MutableObservableMap.putAll] 38 | * * [MutableObservableMap.remove] 39 | * * [MutableObservableMap.replace] 40 | * * [MutableObservableMap.replaceAll] 41 | */ 42 | interface MutableObservableMap: ObservableMap, MutableMap, Observable { 43 | 44 | fun runSilentChanges(silentChanges: MutableObservableMap.() -> Unit) 45 | } 46 | 47 | abstract class AbstractObservableMap protected constructor( 48 | private val mMutableMap: MutableMap, 49 | final override var observer: MapObserver) 50 | : MutableObservableMap, MutableMap by mMutableMap 51 | { 52 | 53 | private val mAdapter = ObservableMapAdapter(observer, mMutableMap) 54 | 55 | override val entries: MutableSet> 56 | = mAdapter.getEntries(mMutableMap::entries) 57 | 58 | override val keys: MutableSet 59 | = mAdapter.getKeys(mMutableMap::keys) 60 | 61 | override val values: MutableCollection 62 | = mAdapter.getValues(mMutableMap::values) 63 | 64 | override fun runSilentChanges(silentChanges: MutableObservableMap.() -> Unit) { 65 | mAdapter.runSilentChanges { silentChanges() } 66 | } 67 | 68 | override fun notifyDataChanged() = mAdapter.notifyDataChanged() 69 | 70 | override fun notifyDataChanged(key: K) = mAdapter.notifyDataChanged(key) 71 | 72 | override fun clear() = mAdapter.clear(mMutableMap::clear) 73 | 74 | override fun put(key: K, value: V): V? = mAdapter.put(key, value, mMutableMap::put) 75 | 76 | override fun putAll(from: Map) = mAdapter.putAll(from, mMutableMap::putAll) 77 | 78 | override fun remove(key: K): V? = mAdapter.remove(key, mMutableMap::remove) 79 | 80 | override fun remove(key: K, value: V): Boolean = mAdapter.remove(key, value, mMutableMap::remove) 81 | 82 | override fun replace(p0: K, p1: V, p2: V): Boolean 83 | = mAdapter.replace(p0, p1, p2, mMutableMap::replace) 84 | 85 | override fun putIfAbsent(p0: K, p1: V): V? = mAdapter.putIfAbsent(p0, p1, mMutableMap::putIfAbsent) 86 | 87 | override fun replace(p0: K, p1: V): V? = mAdapter.replace(p0, p1, mMutableMap::replace) 88 | 89 | override fun replaceAll(p0: BiFunction) 90 | = mAdapter.replaceAll(p0, mMutableMap::replaceAll) 91 | } 92 | 93 | class HashObservableMap(observer: MapObserver, capacity: Int = 16) 94 | : AbstractObservableMap(HashMap(capacity), observer) 95 | { 96 | companion object { 97 | fun create(observer: MapObserver, map: Map) 98 | : HashObservableMap 99 | { 100 | val observerMap = HashObservableMap(observer) 101 | observerMap.putAll(map) 102 | return observerMap 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Creates an empty [MutableObservableMap]. 109 | * 110 | * @return Empty [MutableObservableMap] 111 | */ 112 | fun mutableObservableMapOf(observer: MapObserver): MutableObservableMap = 113 | HashObservableMap(observer) 114 | 115 | /** 116 | * @see [mutableObservableMapOf] 117 | * @see [MapObserverDSL] 118 | */ 119 | fun mutableObservableMapOf(observer: MapObserverDSL.() -> Unit): MutableObservableMap 120 | = mutableObservableMapOf(MapObserverDSL.create(observer)) 121 | 122 | /** 123 | * Creates an [MutableObservableMap] with [elements]. 124 | * 125 | * @return [MutableObservableMap] with [elements] 126 | */ 127 | fun mutableObservableMapOf(observer: MapObserver, 128 | vararg elements: Pair): MutableObservableMap 129 | = HashObservableMap.create(observer, mapOf(*elements)) 130 | 131 | /** 132 | * Creates an [MutableObservableMap] with [map]. 133 | * 134 | * @return [MutableObservableMap] with [map] 135 | */ 136 | fun mutableObservableMapOf(observer: MapObserver, 137 | map: Map): MutableObservableMap 138 | = HashObservableMap.create(observer, map) 139 | 140 | /** 141 | * @see [mutableObservableMapOf] 142 | * @see [MapObserverDSL] 143 | */ 144 | fun mutableObservableMapOf(observer: MapObserverDSL.() -> Unit, 145 | vararg elements: Pair): MutableObservableMap 146 | = mutableObservableMapOf(MapObserverDSL.create(observer), *elements) 147 | 148 | /** 149 | * @see [mutableObservableMapOf] 150 | * @see [MapObserverDSL] 151 | */ 152 | fun mutableObservableMapOf(observer: MapObserverDSL.() -> Unit, 153 | map: Map): MutableObservableMap 154 | = mutableObservableMapOf(MapObserverDSL.create(observer), map) 155 | 156 | /** 157 | * Creates an empty [ObservableMap]. 158 | * 159 | * @return Empty [ObservableMap] 160 | */ 161 | fun observableMapOf(observer: MapObserver): ObservableMap 162 | = mutableObservableMapOf(observer) 163 | 164 | /** 165 | * @see [observableMapOf] 166 | * @see [MapObserverDSL] 167 | */ 168 | fun observableMapOf(observer: MapObserverDSL.() -> Unit): ObservableMap 169 | = mutableObservableMapOf(observer) 170 | 171 | /** 172 | * Creates an [ObservableMap] with [elements]s. 173 | * 174 | * @return [ObservableMap] with [elements]s 175 | */ 176 | fun observableMapOf(observer: MapObserver, 177 | vararg elements: Pair): ObservableMap = 178 | mutableObservableMapOf(observer, *elements) 179 | 180 | /** 181 | * Creates an [ObservableMap] with [map]. 182 | * 183 | * @return [ObservableMap] with [map] 184 | */ 185 | fun observableMapOf(observer: MapObserver, 186 | map: Map): ObservableMap = 187 | mutableObservableMapOf(observer, map) 188 | /** 189 | * @see [observableMapOf] 190 | * @see [MapObserverDSL] 191 | */ 192 | fun observableMapOf(observer: MapObserverDSL.() -> Unit, 193 | vararg elements: Pair): ObservableMap = 194 | mutableObservableMapOf(observer, *elements) 195 | 196 | /** 197 | * @see [observableMapOf] 198 | * @see [MapObserverDSL] 199 | */ 200 | fun observableMapOf(observer: MapObserverDSL.() -> Unit, 201 | map: Map): ObservableMap = 202 | mutableObservableMapOf(observer, map) -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/observable/map/adapter/ObservableMapAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.map.adapter 26 | 27 | import io.bluego.powercollections.getKeyByValue 28 | import io.bluego.powercollections.observable.map.MapObserver 29 | import java.util.function.BiFunction 30 | 31 | open class ObservableMapAdapter>( 32 | private val mObserver: MapObserver, 33 | private val mMap: T) 34 | { 35 | 36 | protected open var isSilent = false 37 | 38 | open fun getEntries(entries: () -> MutableSet>) 39 | : MutableSet> 40 | = ObserverMapSet(entries(), { it.key }, { it.value }, { ObserverMapEntry(it) }) 41 | 42 | open fun getKeys(keys:() -> MutableSet): MutableSet 43 | = ObserverMapSet(keys(), { it }, { mMap[it]!! }, { it }) 44 | 45 | open fun getValues(values: () -> MutableCollection): MutableCollection 46 | = ObserverMapCollection(values(), { mMap.getKeyByValue(it)!! }, { it }, { it }) 47 | 48 | open fun notifyDataChanged() = mObserver.notifyDataChanged() 49 | 50 | open fun notifyDataChanged(key: K) = mObserver.notifyDataChanged(key) 51 | 52 | open fun runSilentChanges(silentChanges: T.() -> Unit) { 53 | isSilent = true 54 | silentChanges(mMap) 55 | isSilent = false 56 | } 57 | 58 | open fun clear(clear: () -> Unit) { 59 | if (!isSilent) { 60 | val map = mMap.toMap() 61 | clear() 62 | mObserver.wasRemoved(map) 63 | } else clear() 64 | } 65 | 66 | open fun put(key: K, value: V, put: (K, V) -> V?): V? { 67 | return if (!isSilent) { 68 | val oldValue: V? = put(key, value) 69 | if (oldValue !== null) mObserver.wasReplaced(key, oldValue, value) 70 | else mObserver.wasAdded(key, value) 71 | oldValue 72 | } else put(key, value) 73 | } 74 | 75 | open fun putAll(from: Map, putAll: (Map) -> Unit) { 76 | if (!isSilent) { 77 | val addedValues = mutableMapOf() 78 | val replacedValues = mutableMapOf>() 79 | from.entries.forEach { (key, value) -> 80 | runSilentChanges { 81 | val oldValue = put(key, value) 82 | if (oldValue !== null) replacedValues[key] = oldValue to value 83 | else addedValues[key] = value 84 | } 85 | } 86 | putAll(from) 87 | if (addedValues.isNotEmpty()) mObserver.wasAdded(addedValues) 88 | if (replacedValues.isNotEmpty()) mObserver.wasReplaced(replacedValues) 89 | } else putAll(from) 90 | } 91 | 92 | open fun remove(key: K, remove: (K) -> V?): V? { 93 | return if (!isSilent) { 94 | val wasRemoved: V? = remove(key) 95 | if (wasRemoved !== null) mObserver.wasRemoved(key, wasRemoved) 96 | wasRemoved 97 | } else remove(key, remove) 98 | } 99 | 100 | open fun remove(key: K, value: V, remove: (K, V) -> Boolean): Boolean { 101 | return if (!isSilent) { 102 | val wasRemoved = remove(key, value) 103 | if (wasRemoved) mObserver.wasRemoved(key, value) 104 | wasRemoved 105 | } else remove(key, value) 106 | } 107 | 108 | open fun replace(p0: K, p1: V, p2: V, replace: (K, V, V) -> Boolean): Boolean { 109 | return if (!isSilent) { 110 | val wasReplaced = replace(p0, p1, p2) 111 | if (wasReplaced) mObserver.wasReplaced(p0, p1, p2) 112 | wasReplaced 113 | } else replace(p0, p1, p2) 114 | } 115 | 116 | @Suppress("UNCHECKED_CAST", "ALWAYS_NULL") 117 | open fun putIfAbsent(p0: K, p1: V, putIfAbsent: (K, V) -> V?): V? { 118 | return if (!isSilent) { 119 | val hasKey = mMap.containsKey(p0) 120 | val oldValue = putIfAbsent(p0, p1) 121 | if (oldValue === null) { 122 | if (hasKey) mObserver.wasReplaced(p0, oldValue as V, p1) 123 | else mObserver.wasAdded(p0, p1) 124 | } 125 | oldValue 126 | } else putIfAbsent(p0, p1) 127 | } 128 | 129 | @Suppress("UNCHECKED_CAST") 130 | open fun replace(p0: K, p1: V, replace: (K, V) -> V?): V? { 131 | return if (!isSilent) { 132 | val oldValue = replace(p0, p1) 133 | if (oldValue !== null || mMap.containsKey(p0)) mObserver.wasReplaced(p0, oldValue as V, p1) 134 | oldValue 135 | } else replace(p0, p1) 136 | } 137 | 138 | //TODO: implement 139 | open fun replaceAll(p0: BiFunction, 140 | replaceAll: (BiFunction) -> Unit) 141 | { 142 | throw NotImplementedError() 143 | } 144 | 145 | protected open inner class ObserverMapCollection( 146 | private val mElements: MutableCollection, 147 | private val mKeyGetter: (T) -> K, 148 | private val mValueGetter: (T) -> V, 149 | private val mElementWrapper: (T) -> T) 150 | : MutableCollection by mElements 151 | { 152 | override fun remove(element: T): Boolean { 153 | val key = mKeyGetter(element) 154 | val value = mValueGetter(element) 155 | val result = mElements.remove(element) 156 | if (result) mObserver.wasRemoved(key, value) 157 | return result 158 | } 159 | 160 | //TODO: implement 161 | override fun retainAll(elements: Collection): Boolean { 162 | throw NotImplementedError() 163 | } 164 | 165 | //TODO: implement 166 | override fun removeAll(elements: Collection): Boolean { 167 | throw NotImplementedError() 168 | } 169 | 170 | override fun clear() = mElements.clear() 171 | 172 | override fun iterator(): MutableIterator { 173 | val iterator = mElements.iterator() 174 | return ObserverMapCollectionIterator(iterator, mKeyGetter, mValueGetter, mElementWrapper) 175 | } 176 | 177 | protected open inner class ObserverMapCollectionIterator( 178 | private val mIterator: MutableIterator, 179 | private val mKeyGetter: (T) -> K, 180 | private val mValueGetter: (T) -> V, 181 | private val mElementWrapper: (T) -> T) 182 | : MutableIterator 183 | { 184 | 185 | private var mLast: T? = null 186 | private var mNextWasCalled = false 187 | 188 | override fun hasNext(): Boolean = mIterator.hasNext() 189 | 190 | override fun next(): T { 191 | val element = mIterator.next().apply { mLast = this } 192 | mNextWasCalled = true 193 | return mElementWrapper(element) 194 | } 195 | 196 | @Suppress("UNCHECKED_CAST") 197 | override fun remove() { 198 | if (!mNextWasCalled) throw IllegalStateException("Call Iterator.next()") 199 | else mNextWasCalled = false 200 | val key = mKeyGetter(mLast as T) 201 | val value = mValueGetter(mLast as T) 202 | try { 203 | mIterator.remove() 204 | mObserver.wasRemoved(key, value) 205 | } finally { 206 | mLast = null 207 | } 208 | } 209 | } 210 | } 211 | 212 | protected open inner class ObserverMapSet( 213 | private val mElements: MutableSet, 214 | private val mKeyGetter: (T) -> K, 215 | private val mValueGetter: (T) -> V, 216 | private val mElementWrapper: (T) -> T) 217 | : MutableSet by mElements 218 | { 219 | override fun remove(element: T): Boolean { 220 | val key = mKeyGetter(element) 221 | val value = mValueGetter(element) 222 | val result = mElements.remove(element) 223 | if (result) mObserver.wasRemoved(key, value) 224 | return result 225 | } 226 | 227 | override fun clear() = mElements.clear() 228 | 229 | override fun iterator(): MutableIterator { 230 | val iterator = mElements.iterator() 231 | return ObserverMapSetIterator(iterator, mKeyGetter, mValueGetter, mElementWrapper) 232 | } 233 | 234 | override fun retainAll(elements: Collection): Boolean { 235 | throw NotImplementedError() 236 | } 237 | 238 | override fun removeAll(elements: Collection): Boolean { 239 | throw NotImplementedError() 240 | } 241 | 242 | protected open inner class ObserverMapSetIterator( 243 | private val mIterator: MutableIterator, 244 | private val mKeyGetter: (T) -> K, 245 | private val mValueGetter: (T) -> V, 246 | private val mElementWrapper: (T) -> T) 247 | : MutableIterator 248 | { 249 | 250 | private var mLast: T? = null 251 | private var mNextWasCalled = false 252 | 253 | override fun hasNext(): Boolean = mIterator.hasNext() 254 | 255 | override fun next(): T { 256 | val element = mIterator.next().apply { mLast = this } 257 | return mElementWrapper(element) 258 | } 259 | 260 | @Suppress("UNCHECKED_CAST") 261 | override fun remove() { 262 | if (!mNextWasCalled) IllegalStateException("Call Iterator.next()") 263 | else mNextWasCalled = false 264 | val key = mKeyGetter(mLast as T) 265 | val value = mValueGetter(mLast as T) 266 | try { 267 | mIterator.remove() 268 | mObserver.wasRemoved(key, value) 269 | } finally { 270 | mLast = null 271 | } 272 | } 273 | } 274 | } 275 | 276 | protected open inner class ObserverMapEntry(private val mEntry: MutableMap.MutableEntry) 277 | : MutableMap.MutableEntry by mEntry 278 | { 279 | override fun setValue(newValue: V): V { 280 | val oldValue = mEntry.setValue(newValue) 281 | mObserver.wasReplaced(mEntry.key, oldValue, newValue) 282 | return oldValue 283 | } 284 | } 285 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/weak/WeakCollection.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak 26 | 27 | import io.bluego.powercollections.weak.adapter.WeakCollectionAdapter 28 | import io.bluego.powercollections.weak.adapter.WeakElement 29 | import java.lang.ref.WeakReference 30 | 31 | /** 32 | * All elements of the [Collection] are wrapped in a [WeakReference]. 33 | */ 34 | interface WeakCollection: Collection, Weakable 35 | 36 | /** 37 | * All elements of the [MutableCollection] are wrapped in a [WeakReference]. 38 | */ 39 | interface MutableWeakCollection: WeakCollection, MutableCollection 40 | 41 | abstract class AbstractWeakCollection protected constructor( 42 | private val mCollection: MutableCollection>) 43 | : MutableWeakCollection 44 | { 45 | 46 | constructor(): this(arrayListOf()) 47 | 48 | private val mAdapter = WeakCollectionAdapter(mCollection) 49 | 50 | override fun add(element: T?): Boolean = mAdapter.add(element, mCollection::add) 51 | 52 | override fun addAll(elements: Collection): Boolean = mAdapter.addAll(elements, mCollection::addAll) 53 | 54 | override fun clear() = mAdapter.clear(mCollection::clear) 55 | 56 | override fun iterator(): MutableIterator = mAdapter.iterator(mCollection::iterator) 57 | 58 | override fun remove(element: T?): Boolean = mAdapter.remove(element, mCollection::remove) 59 | 60 | override fun removeAll(elements: Collection): Boolean = mAdapter.removeAll(elements, mCollection::removeAll) 61 | 62 | override fun retainAll(elements: Collection): Boolean = mAdapter.retainAll(elements, mCollection::retainAll) 63 | 64 | override val size: Int 65 | get() = mAdapter.size(mCollection::size) 66 | 67 | override fun contains(element: T?): Boolean = mAdapter.contains(element, mCollection::contains) 68 | 69 | override fun containsAll(elements: Collection): Boolean = mAdapter.containsAll(elements, mCollection::containsAll) 70 | 71 | override fun isEmpty(): Boolean = mAdapter.isEmpty(mCollection::isEmpty) 72 | 73 | override fun optimize() = mAdapter.optimize() 74 | 75 | override fun forceOptimize() = mAdapter.forceOptimize() 76 | } 77 | 78 | class HashWeakCollection(capacity: Int = 16) : AbstractWeakCollection(ArrayList(capacity)) { 79 | 80 | companion object { 81 | fun create(list: List) : HashWeakCollection { 82 | val weakCollection = HashWeakCollection() 83 | weakCollection.addAll(list) 84 | return weakCollection 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Creates an empty [MutableWeakCollection]. 91 | * 92 | * @return Empty [MutableWeakCollection] 93 | */ 94 | fun mutableWeakCollectionOf(): MutableWeakCollection = HashWeakCollection() 95 | 96 | /** 97 | * Creates an [MutableWeakCollection] with [elements]s. 98 | * 99 | * @return [MutableWeakCollection] with [elements]s 100 | */ 101 | fun mutableWeakCollectionOf(vararg elements: T): MutableWeakCollection 102 | = HashWeakCollection.create(arrayListOf(*elements)) 103 | 104 | /** 105 | * Creates an empty [WeakCollection]. 106 | * 107 | * @return Empty [WeakCollection] 108 | */ 109 | fun weakCollectionOf(): WeakCollection = mutableWeakCollectionOf() 110 | 111 | /** 112 | * Creates an [WeakCollection] with [elements]s. 113 | * 114 | * @return [WeakCollection] with [elements]s 115 | */ 116 | fun weakCollectionOf(vararg elements: T): WeakCollection 117 | = mutableWeakCollectionOf(*elements) 118 | 119 | -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/weak/WeakSet.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak 26 | 27 | import io.bluego.powercollections.weak.adapter.WeakCollectionAdapter 28 | import io.bluego.powercollections.weak.adapter.WeakElement 29 | import java.lang.ref.WeakReference 30 | 31 | /** 32 | * All elements of the [Set] are wrapped in a [WeakReference]. 33 | */ 34 | interface WeakSet: Set, WeakCollection 35 | 36 | /** 37 | * All elements of the [MutableSet] are wrapped in a [WeakReference]. 38 | */ 39 | interface MutableWeakSet: WeakSet, MutableSet, MutableWeakCollection 40 | 41 | abstract class AbstractWeakSet protected constructor( 42 | private val mSet: MutableSet>) 43 | : MutableWeakSet { 44 | 45 | constructor() : this(hashSetOf()) 46 | 47 | private val mAdapter = WeakCollectionAdapter(mSet) 48 | 49 | override fun add(element: T?): Boolean = mAdapter.add(element, mSet::add) 50 | 51 | override fun addAll(elements: Collection): Boolean = mAdapter.addAll(elements, mSet::addAll) 52 | 53 | override fun clear() = mAdapter.clear(mSet::clear) 54 | 55 | override fun iterator(): MutableIterator = mAdapter.iterator(mSet::iterator) 56 | 57 | override fun remove(element: T?): Boolean = mAdapter.remove(element, mSet::remove) 58 | 59 | override fun removeAll(elements: Collection): Boolean = mAdapter.removeAll(elements, mSet::removeAll) 60 | 61 | override fun retainAll(elements: Collection): Boolean = mAdapter.retainAll(elements, mSet::retainAll) 62 | 63 | override val size: Int 64 | get() = mAdapter.size(mSet::size) 65 | 66 | override fun contains(element: T?): Boolean = mAdapter.contains(element, mSet::contains) 67 | 68 | override fun containsAll(elements: Collection): Boolean = mAdapter.containsAll(elements, mSet::containsAll) 69 | 70 | override fun isEmpty(): Boolean = mAdapter.isEmpty(mSet::isEmpty) 71 | 72 | override fun optimize() = mAdapter.optimize() 73 | 74 | override fun forceOptimize() = mAdapter.forceOptimize() 75 | } 76 | 77 | class HashWeakSet(capacity: Int = 16) : AbstractWeakSet(HashSet(capacity)) { 78 | 79 | companion object { 80 | fun create(set: Set) : HashWeakSet { 81 | val weakSet = HashWeakSet() 82 | weakSet.addAll(set) 83 | return weakSet 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Creates an empty [MutableWeakSet]. 90 | * 91 | * @return Empty [MutableWeakSet] 92 | */ 93 | fun mutableWeakSetOf(): MutableWeakSet = HashWeakSet() 94 | 95 | /** 96 | * Creates an [MutableWeakSet] with [elements]s. 97 | * 98 | * @return [MutableWeakSet] with [elements]s 99 | */ 100 | fun mutableWeakSetOf(vararg elements: T): MutableWeakSet 101 | = HashWeakSet.create(setOf(*elements)) 102 | 103 | /** 104 | * Creates an empty [WeakSet]. 105 | * 106 | * @return Empty [WeakSet] 107 | */ 108 | fun weakSetOf(): WeakSet = mutableWeakSetOf() 109 | 110 | /** 111 | * Creates an [WeakSet] with [elements]s. 112 | * 113 | * @return [WeakSet] with [elements]s 114 | */ 115 | fun weakSetOf(vararg elements: T): WeakSet 116 | = mutableWeakSetOf(*elements) 117 | -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/weak/Weakable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak 26 | 27 | interface Weakable { 28 | 29 | /** 30 | * Removes all empty items that has been garbage collected. 31 | */ 32 | fun optimize() 33 | 34 | /** 35 | * Removes all empty items that has been garbage collected. 36 | */ 37 | fun forceOptimize() 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/bluego/powercollections/weak/adapter/WeakCollectionAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak.adapter 26 | 27 | import java.lang.ref.ReferenceQueue 28 | import java.lang.ref.WeakReference 29 | 30 | internal open class WeakCollectionAdapter>>(private val mCollection: T) { 31 | 32 | private val mQueue = ReferenceQueue() 33 | 34 | open fun add(element: E?, add: (WeakElement) -> Boolean): Boolean { 35 | processQueue() 36 | return WeakElement.create(element, mQueue)?.let(add) ?: false 37 | } 38 | 39 | open fun addAll(elements: Collection, addAll: (Collection>) -> Boolean): Boolean { 40 | processQueue() 41 | return elements.mapNotNull { WeakElement.create(it, mQueue) }.let { 42 | if (it.isEmpty()) false 43 | else addAll(it) 44 | } 45 | } 46 | 47 | open fun clear(clear: () -> Unit) = clear() 48 | 49 | open fun iterator(iterator: () -> MutableIterator>): MutableIterator { 50 | processQueue() 51 | return ReferenceIterator(iterator()) 52 | } 53 | 54 | open fun remove(element: E?, remove: (WeakElement) -> Boolean): Boolean 55 | = WeakElement.create(element)?.let(remove) ?: false 56 | 57 | open fun removeAll(elements: Collection, removeAll: (Collection>) -> Boolean): Boolean 58 | = removeAll(elements.mapNotNull { WeakElement.create(it) }).apply { processQueue() } 59 | 60 | open fun retainAll(elements: Collection, retainAll: (Collection>) -> Boolean): Boolean 61 | = retainAll(elements.mapNotNull { WeakElement.Companion.create(it) }).apply { processQueue() } 62 | 63 | open fun size(size: () -> Int): Int { 64 | processQueue() 65 | return size() 66 | } 67 | 68 | open fun contains(element: E?, contains: (WeakElement) -> Boolean): Boolean 69 | = WeakElement.create(element)?.let(contains) ?: false 70 | 71 | open fun containsAll(elements: Collection, containsAll: (Collection>) -> Boolean): Boolean { 72 | val weakElements = elements.mapNotNull { WeakElement.create(it) } 73 | return containsAll(weakElements) 74 | } 75 | 76 | open fun isEmpty(isEmpty: () -> Boolean): Boolean = isEmpty() 77 | 78 | open fun optimize() = processQueue() 79 | 80 | open fun forceOptimize() { 81 | mCollection.iterator().let { 82 | while (it.hasNext()) { it.next().get() ?: it.remove() } 83 | } 84 | } 85 | 86 | @Suppress("UNCHECKED_CAST") 87 | private fun processQueue() { 88 | do mQueue.poll()?.apply { mCollection.remove(this as WeakElement) } ?: return 89 | while (true) 90 | } 91 | 92 | private inner class ReferenceIterator(private val mIterator: MutableIterator>) 93 | : MutableIterator 94 | { 95 | override fun hasNext(): Boolean = mIterator.hasNext() 96 | 97 | override fun next(): E? = mIterator.next().get() 98 | 99 | override fun remove() = mIterator.remove() 100 | } 101 | } 102 | 103 | class WeakElement : WeakReference 104 | { 105 | private var hash: Int = 0 /* Hashcode of key, stored here since the key 106 | may be tossed by the GC */ 107 | 108 | 109 | private constructor(element: T) : super(element) { 110 | hash = element!!.hashCode() 111 | } 112 | 113 | private constructor(element: T, q: ReferenceQueue) : super(element, q) { 114 | hash = element!!.hashCode() 115 | } 116 | 117 | companion object { 118 | 119 | fun create(element: T?): WeakElement? = 120 | if (element === null) null else WeakElement(element) 121 | 122 | fun create(element: T?, queue: ReferenceQueue): WeakElement? = 123 | if (element === null) null else WeakElement(element, queue) 124 | 125 | } 126 | 127 | override fun equals(other: Any?): Boolean { 128 | if (this === other) return true 129 | if (other !is WeakElement<*>) return false 130 | val t = this.get() 131 | val u = other.get() 132 | if (t === u) return true 133 | return if (t == null || u == null) false else t == u 134 | } 135 | 136 | override fun hashCode(): Int = hash 137 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/BiMapTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections 26 | 27 | import io.bluego.powercollections._testutils.templates.MapTest 28 | import kotlin.test.Test 29 | import kotlin.test.assertEquals 30 | import kotlin.test.assertFailsWith 31 | 32 | @Suppress("PropertyName") 33 | class BiMapTest: MapTest() { 34 | 35 | override val `map of 3 different items`: LinkedHashMap 36 | get() = LinkedHashMap(mapOf(0 to "zero", 1 to "one", 2 to "two")) 37 | 38 | override val `map of 4 different items`: LinkedHashMap 39 | get() = LinkedHashMap(mapOf(10 to "ten", 11 to "eleven", 12 to "twelve", 13 to "thirteen")) 40 | 41 | override val `get map to test`: () -> MutableBiMap 42 | get() = { mutableBiMapOf() } 43 | 44 | 45 | override fun `test mutableEntry`() { 46 | 47 | val map = `get map to test`() 48 | 49 | map.putAll(`map of 3 different items`) 50 | 51 | assertEquals(`map of 3 different items`.entries, map.entries) 52 | 53 | map.entries.forEachIndexed { index, mutableEntry -> 54 | mutableEntry.setValue(`map of 4 different items`.at(index).value) 55 | } 56 | 57 | assertEquals(`map of 3 different items`.entries.mapIndexed { index, mutableEntry -> mutableEntry.key to `map of 4 different items`.valueAt(index) }, 58 | map.entries.map(Map.Entry::toPair)) 59 | } 60 | 61 | @Test 62 | fun `test put existing key`() { 63 | 64 | val map = `get map to test`() 65 | 66 | map.putAll(`map of 3 different items`) 67 | 68 | map[`map of 3 different items`.keyAt(0)] = `map of 4 different items`.valueAt(0) 69 | 70 | val referenceMap = `map of 3 different items` 71 | referenceMap[referenceMap.keyAt(0)] = `map of 4 different items`.valueAt(0) 72 | 73 | assertEquals>(referenceMap, map) 74 | } 75 | 76 | @Test 77 | fun `test put existing value`() { 78 | 79 | val map = `get map to test`() 80 | 81 | map.putAll(`map of 3 different items`) 82 | 83 | assertFailsWith(IllegalArgumentException::class) { 84 | map[`map of 4 different items`.keyAt(0)] = `map of 3 different items`.valueAt(0) 85 | } 86 | 87 | assertEquals>(`map of 3 different items`, map) 88 | 89 | } 90 | 91 | @Test 92 | fun `test iterator setValue existing value`() { 93 | 94 | val map = `get map to test`() 95 | 96 | map.putAll(`map of 3 different items`) 97 | 98 | val iter = map.iterator() 99 | 100 | iter.next().setValue(`map of 4 different items`.valueAt(0)) 101 | 102 | assertFailsWith(IllegalArgumentException::class) { 103 | 104 | iter.next().setValue(`map of 3 different items`.valueAt(0)) 105 | } 106 | 107 | val referenceMap = `map of 3 different items` 108 | referenceMap[referenceMap.keyAt(0)] = `map of 4 different items`.valueAt(0) 109 | 110 | assertEquals>(referenceMap, map) 111 | 112 | } 113 | 114 | @Test 115 | fun `test inverse`() { 116 | 117 | val map = `get map to test`() 118 | 119 | map.putAll(`map of 3 different items`) 120 | 121 | assertEquals>(`map of 3 different items`, map) 122 | 123 | 124 | val inversedMap = `map of 3 different items`.map { it.value to it.key }.toMap() 125 | 126 | assertEquals(inversedMap, map.inverse) 127 | } 128 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/PowerCollectionsTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections 26 | 27 | import io.bluego.powercollections.bounded.* 28 | import io.bluego.powercollections.observable.list.MutableObservableList 29 | import io.bluego.powercollections.observable.list.ObservableList 30 | import io.bluego.powercollections.observable.list.mutableObservableListOf 31 | import io.bluego.powercollections.observable.list.observableListOf 32 | import io.bluego.powercollections.observable.map.MutableObservableMap 33 | import io.bluego.powercollections.observable.map.ObservableMap 34 | import io.bluego.powercollections.observable.map.mutableObservableMapOf 35 | import io.bluego.powercollections.observable.map.observableMapOf 36 | import io.bluego.powercollections.weak.* 37 | import org.junit.Test 38 | import kotlin.test.assertEquals 39 | 40 | class PowerCollectionsTest { 41 | 42 | 43 | @Test 44 | fun `test biMap`() { 45 | 46 | val sampleMap0 = mapOf(0 to "zero") 47 | val sampleMap1 = mapOf(10 to "ten", 11 to "eleven") 48 | 49 | var mutableBiMap: MutableBiMap by PowerCollections.biMap() 50 | var biMap: BiMap by PowerCollections.biMap() 51 | var mutableMap: MutableMap by PowerCollections.biMap() 52 | var map: Map by PowerCollections.biMap() 53 | 54 | mutableBiMap.putAll(sampleMap0) 55 | mutableMap.putAll(sampleMap0) 56 | assertEquals(sampleMap0, mutableBiMap) 57 | assertEquals(sampleMap0, mutableMap) 58 | 59 | mutableBiMap = mutableBiMapOf(sampleMap1) 60 | biMap = biMapOf(sampleMap1) 61 | mutableMap = mutableMapOf(*sampleMap1.toList().toTypedArray()) 62 | map = mapOf(*sampleMap1.toList().toTypedArray()) 63 | 64 | assertEquals(sampleMap1, mutableBiMap) 65 | assertEquals(sampleMap1, biMap) 66 | assertEquals(sampleMap1, mutableMap) 67 | assertEquals(sampleMap1, map) 68 | 69 | } 70 | 71 | @Test 72 | fun `test boundedList`() { 73 | 74 | val sampleList0 = listOf("zero") 75 | val sampleList1 = listOf("ten", "eleven") 76 | 77 | var mutableBoundedList: MutableBoundedList by PowerCollections.boundedList(2) 78 | var boundedList: BoundedList by PowerCollections.boundedList(2) 79 | var mutableList: MutableList by PowerCollections.boundedList(2) 80 | var list: List by PowerCollections.boundedList(2) 81 | 82 | mutableBoundedList.addAll(sampleList0) 83 | mutableList.addAll(sampleList0) 84 | assertEquals(sampleList0, mutableBoundedList) 85 | assertEquals(sampleList0, mutableList) 86 | 87 | mutableBoundedList = mutableBoundedListOf(2, *sampleList1.toTypedArray()) 88 | boundedList = boundedListOf(2, *sampleList1.toTypedArray()) 89 | mutableList = mutableListOf(*sampleList1.toTypedArray()) 90 | list = listOf(*sampleList1.toTypedArray()) 91 | 92 | assertEquals(sampleList1, mutableBoundedList) 93 | assertEquals(sampleList1, boundedList) 94 | assertEquals(sampleList1, mutableList) 95 | assertEquals(sampleList1, list) 96 | } 97 | 98 | @Test 99 | fun `test boundedMap`() { 100 | 101 | val sampleMap0 = mapOf(0 to "zero") 102 | val sampleMap1 = mapOf(10 to "ten", 11 to "eleven") 103 | 104 | var mutableBoundedMap: MutableBoundedMap by PowerCollections.boundedMap(2) 105 | var boundedMap: BoundedMap by PowerCollections.boundedMap(2) 106 | var mutableMap: MutableMap by PowerCollections.boundedMap(2) 107 | var map: Map by PowerCollections.boundedMap(2) 108 | 109 | mutableBoundedMap.putAll(sampleMap0) 110 | mutableMap.putAll(sampleMap0) 111 | assertEquals(sampleMap0, mutableBoundedMap) 112 | assertEquals(sampleMap0, mutableMap) 113 | 114 | mutableBoundedMap = mutableBoundedMapOf(2, sampleMap1) 115 | boundedMap = boundedMapOf(2, sampleMap1) 116 | mutableMap = mutableMapOf(*sampleMap1.toList().toTypedArray()) 117 | map = mapOf(*sampleMap1.toList().toTypedArray()) 118 | 119 | assertEquals(sampleMap1, mutableBoundedMap) 120 | assertEquals(sampleMap1, boundedMap) 121 | assertEquals(sampleMap1, mutableMap) 122 | assertEquals(sampleMap1, map) 123 | } 124 | 125 | @Test 126 | fun `test observableList`() { 127 | 128 | val sampleList0 = listOf("zero") 129 | val sampleList1 = listOf("ten", "eleven") 130 | 131 | var mutableObservableList: MutableObservableList by PowerCollections.observableList { safetyMode = false } 132 | var observableList: ObservableList by PowerCollections.observableList { safetyMode = false } 133 | var mutableList: MutableList by PowerCollections.observableList { safetyMode = false } 134 | var list: List by PowerCollections.observableList { safetyMode = false } 135 | 136 | mutableObservableList.addAll(sampleList0) 137 | mutableList.addAll(sampleList0) 138 | assertEquals(sampleList0, mutableObservableList) 139 | assertEquals(sampleList0, mutableList) 140 | 141 | mutableObservableList = mutableObservableListOf({ safetyMode = false }, *sampleList1.toTypedArray()) 142 | observableList = observableListOf({ safetyMode = false }, *sampleList1.toTypedArray()) 143 | mutableList = mutableListOf(*sampleList1.toList().toTypedArray()) 144 | list = listOf(*sampleList1.toList().toTypedArray()) 145 | 146 | assertEquals(sampleList1, mutableObservableList) 147 | assertEquals(sampleList1, observableList) 148 | assertEquals(sampleList1, mutableList) 149 | assertEquals(sampleList1, list) 150 | } 151 | 152 | @Test 153 | fun `test observableMap`() { 154 | 155 | val sampleMap0 = mapOf(0 to "zero") 156 | val sampleMap1 = mapOf(10 to "ten", 11 to "eleven") 157 | 158 | var mutableObservableMap: MutableObservableMap by PowerCollections.observableMap { safetyMode = false } 159 | var observableMap: ObservableMap by PowerCollections.observableMap { safetyMode = false } 160 | var mutableMap: MutableMap by PowerCollections.observableMap { safetyMode = false } 161 | var map: Map by PowerCollections.observableMap { safetyMode = false } 162 | 163 | mutableObservableMap.putAll(sampleMap0) 164 | mutableMap.putAll(sampleMap0) 165 | assertEquals(sampleMap0, mutableObservableMap) 166 | assertEquals(sampleMap0, mutableMap) 167 | 168 | mutableObservableMap = mutableObservableMapOf({ safetyMode = false }, sampleMap1) 169 | observableMap = observableMapOf({ safetyMode = false }, sampleMap1) 170 | mutableMap = mutableMapOf(*sampleMap1.toList().toTypedArray()) 171 | map = mapOf(*sampleMap1.toList().toTypedArray()) 172 | 173 | assertEquals(sampleMap1, mutableObservableMap) 174 | assertEquals(sampleMap1, observableMap) 175 | assertEquals(sampleMap1, mutableMap) 176 | assertEquals(sampleMap1, map) 177 | } 178 | 179 | @Test 180 | fun `test weakCollection`() { 181 | 182 | val sampleList0: Collection = listOf("zero") 183 | val sampleList1: Collection = listOf("ten", "eleven") 184 | 185 | var mutableWeakCollection: MutableWeakCollection by PowerCollections.weakCollection() 186 | var weakCollection: WeakCollection by PowerCollections.weakCollection() 187 | var mutableCollection: MutableCollection by PowerCollections.weakCollection() 188 | var collection: Collection by PowerCollections.weakCollection() 189 | 190 | mutableWeakCollection.addAll(sampleList0) 191 | mutableCollection.addAll(sampleList0) 192 | 193 | assertEquals(sampleList0, mutableWeakCollection.toList()) 194 | assertEquals(sampleList0, mutableCollection.toList()) 195 | 196 | mutableWeakCollection = mutableWeakCollectionOf(*sampleList1.toTypedArray()) 197 | weakCollection = weakCollectionOf(*sampleList1.toTypedArray()) 198 | mutableCollection = mutableListOf(*sampleList1.toTypedArray()) 199 | collection = listOf(*sampleList1.toTypedArray()) 200 | 201 | assertEquals(sampleList1, mutableWeakCollection.toList()) 202 | assertEquals(sampleList1, weakCollection.toList()) 203 | assertEquals(sampleList1, mutableCollection.toList()) 204 | assertEquals(sampleList1, collection.toList()) 205 | } 206 | 207 | @Test 208 | fun `test weakSet`() { 209 | 210 | val sampleList0: Set = setOf("zero") 211 | val sampleList1: Set = setOf("ten", "eleven") 212 | 213 | var mutableWeakSet: MutableWeakSet by PowerCollections.weakSet() 214 | var weakSet: WeakSet by PowerCollections.weakSet() 215 | var mutableSet: MutableSet by PowerCollections.weakSet() 216 | var set: Set by PowerCollections.weakSet() 217 | 218 | mutableWeakSet.addAll(sampleList0) 219 | mutableSet.addAll(sampleList0) 220 | 221 | assertEquals(sampleList0, mutableWeakSet) 222 | assertEquals(sampleList0, mutableSet) 223 | 224 | mutableWeakSet = mutableWeakSetOf(*sampleList1.toTypedArray()) 225 | weakSet = weakSetOf(*sampleList1.toTypedArray()) 226 | mutableSet = mutableSetOf(*sampleList1.toTypedArray()) 227 | set = setOf(*sampleList1.toTypedArray()) 228 | 229 | assertEquals(sampleList1, mutableWeakSet.toSet()) 230 | assertEquals(sampleList1, weakSet.toSet()) 231 | assertEquals(sampleList1, mutableSet) 232 | assertEquals(sampleList1, set) 233 | } 234 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/_testutils/Utils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections._testutils 26 | 27 | import kotlin.test.assertEquals 28 | 29 | fun Collection<*>.assertSize(size: Int) { 30 | assertEquals(size, this.size) 31 | } 32 | -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/_testutils/templates/CollectionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections._testutils.templates 26 | 27 | import io.bluego.powercollections._testutils.assertSize 28 | import org.junit.Test 29 | import kotlin.test.assertEquals 30 | import kotlin.test.assertFailsWith 31 | import kotlin.test.assertFalse 32 | import kotlin.test.assertTrue 33 | 34 | @Suppress("LocalVariableName", "PropertyName") 35 | abstract class CollectionTest { 36 | 37 | protected abstract val `list of 3 different items`: List 38 | 39 | protected abstract val `list of 4 different items`: List 40 | 41 | protected abstract val `get collection to test`: () -> MutableCollection 42 | 43 | 44 | init { 45 | @Suppress("LeakingThis") 46 | assert(`list of 3 different items`.size == 3) 47 | @Suppress("LeakingThis") 48 | assert(`list of 4 different items`.size == 4) 49 | } 50 | 51 | @Test 52 | open fun `test addAll`() { 53 | 54 | val collection = `get collection to test`() 55 | 56 | assertTrue { collection.addAll(`list of 3 different items`) } 57 | 58 | collection.assertSize(3) 59 | 60 | } 61 | 62 | @Test 63 | open fun `test size`() { 64 | 65 | val collection = `get collection to test`() 66 | 67 | collection.assertSize(0) 68 | 69 | assertTrue { collection.addAll(`list of 3 different items`) } 70 | 71 | collection.assertSize(3) 72 | } 73 | 74 | @Test 75 | open fun `test remove`() { 76 | 77 | val collection = `get collection to test`() 78 | 79 | assertTrue { collection.addAll(`list of 3 different items`) } 80 | 81 | collection.assertSize(3) 82 | 83 | assertTrue { collection.remove(`list of 3 different items`[0]) } 84 | 85 | collection.assertSize(2) 86 | 87 | assertTrue { collection.remove(`list of 3 different items`[1]) } 88 | 89 | collection.assertSize(1) 90 | 91 | assertTrue { collection.remove(`list of 3 different items`[2]) } 92 | 93 | collection.assertSize(0) 94 | 95 | assertFalse { collection.remove(`list of 3 different items`[2]) } 96 | 97 | collection.assertSize(0) 98 | 99 | } 100 | 101 | @Test 102 | open fun `test removeIf`() { 103 | 104 | val collection = `get collection to test`() 105 | 106 | assertTrue { collection.addAll(`list of 3 different items`) } 107 | 108 | collection.assertSize(3) 109 | 110 | assertTrue(collection.removeIf { it == `list of 3 different items`[0] }) 111 | 112 | collection.assertSize(2) 113 | 114 | assertFalse(collection.removeIf { it == `list of 3 different items`[0] }) 115 | 116 | collection.assertSize(2) 117 | 118 | } 119 | 120 | @Test 121 | open fun `test add`() { 122 | 123 | val collection = `get collection to test`() 124 | 125 | assertTrue { collection.add(`list of 4 different items`[0]) } 126 | 127 | assertTrue { collection.add(`list of 4 different items`[1]) } 128 | 129 | assertTrue { collection.add(`list of 4 different items`[2]) } 130 | 131 | collection.assertSize(3) 132 | 133 | } 134 | 135 | @Test 136 | open fun `test contains`() { 137 | 138 | val collection = `get collection to test`() 139 | 140 | assertTrue { collection.addAll(`list of 3 different items`.drop(1)) } 141 | 142 | assertTrue { collection.contains(`list of 3 different items`[1]) } 143 | 144 | assertTrue { collection.contains(`list of 3 different items`[2]) } 145 | 146 | assertFalse { collection.contains(`list of 3 different items`[0]) } 147 | 148 | } 149 | 150 | @Test 151 | open fun `test containsAll`() { 152 | 153 | val collection = `get collection to test`() 154 | 155 | assertTrue { collection.addAll(`list of 3 different items`) } 156 | 157 | assertTrue { collection.containsAll(`list of 3 different items`) } 158 | 159 | assertFalse { collection.containsAll(`list of 4 different items`) } 160 | } 161 | 162 | @Test 163 | open fun `test isEmpty`() { 164 | 165 | val collection = `get collection to test`() 166 | 167 | assertTrue { collection.addAll(`list of 3 different items`) } 168 | 169 | assertFalse { collection.isEmpty() } 170 | 171 | assertEquals(Unit, collection.clear()) 172 | 173 | assertTrue { collection.isEmpty() } 174 | 175 | } 176 | 177 | @Test 178 | open fun `test iterator`() { 179 | 180 | val collection = `get collection to test`() 181 | 182 | assertTrue { collection.addAll(`list of 3 different items`) } 183 | 184 | val iterator = collection.iterator() 185 | 186 | collection.assertSize(3) 187 | 188 | repeat(3) { 189 | 190 | assertTrue { iterator.hasNext() } 191 | 192 | if (it < 3) assertEquals(`list of 3 different items`[it], iterator.next()) 193 | else assertFailsWith(ArrayIndexOutOfBoundsException::class) { iterator.next() } 194 | 195 | assertEquals(Unit, iterator.remove()) 196 | 197 | collection.assertSize(3 - (it + 1)) 198 | } 199 | 200 | collection.assertSize(0) 201 | } 202 | 203 | @Test 204 | open fun `test clear`() { 205 | 206 | val collection = `get collection to test`() 207 | 208 | collection.assertSize(0) 209 | 210 | assertTrue { collection.addAll(`list of 3 different items`) } 211 | 212 | collection.assertSize(3) 213 | 214 | assertEquals(Unit, collection.clear()) 215 | 216 | collection.assertSize(0) 217 | } 218 | 219 | @Test 220 | open fun `test retainAll`() { 221 | 222 | val collection = `get collection to test`() 223 | 224 | assertTrue { collection.addAll(`list of 3 different items`) } 225 | 226 | collection.assertSize(3) 227 | 228 | assertTrue { collection.retainAll(`list of 3 different items`.drop(2)) } 229 | 230 | collection.assertSize(1) 231 | 232 | assertEquals(`list of 3 different items`[2], collection.first()) 233 | 234 | assertFalse { collection.retainAll(`list of 3 different items`) } 235 | 236 | collection.assertSize(1) 237 | 238 | } 239 | 240 | @Test 241 | open fun `test removeAll`() { 242 | 243 | val `first collection` = `get collection to test`() 244 | 245 | assertTrue { `first collection`.addAll(`list of 3 different items`) } 246 | 247 | `first collection`.assertSize(3) 248 | 249 | assertTrue { `first collection`.removeAll(`list of 3 different items`) } 250 | 251 | `first collection`.assertSize(0) 252 | 253 | 254 | val `second collection` = `get collection to test`() 255 | 256 | assertTrue { `second collection`.addAll(`list of 3 different items`.take(2)) } 257 | 258 | `second collection`.assertSize(2) 259 | 260 | assertFalse { `second collection`.removeAll(`list of 4 different items`) } 261 | 262 | `second collection`.assertSize(2) 263 | 264 | } 265 | 266 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/_testutils/templates/ListTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections._testutils.templates 26 | 27 | import io.bluego.powercollections._testutils.assertSize 28 | import org.junit.Test 29 | import kotlin.test.assertEquals 30 | import kotlin.test.assertFailsWith 31 | import kotlin.test.assertTrue 32 | 33 | @Suppress("RemoveRedundantBackticks", "PropertyName", "LocalVariableName") 34 | abstract class ListTest: CollectionTest() { 35 | 36 | 37 | abstract override val `list of 3 different items`: List 38 | 39 | abstract override val `list of 4 different items`: List 40 | 41 | abstract override val `get collection to test`: () -> MutableList 42 | 43 | 44 | @Test 45 | open fun `test addAllByIndex`() { 46 | 47 | val list = `get collection to test`() 48 | 49 | val `first item` = `list of 3 different items`.first() 50 | 51 | val `collection from 1 to 2` = `list of 3 different items`.drop(1) 52 | 53 | assertTrue { list.add(`first item`) } 54 | 55 | list.assertSize(1) 56 | 57 | assertTrue { list.addAll(0, `collection from 1 to 2`) } 58 | 59 | assertEquals(`collection from 1 to 2`[0], list[0]) 60 | 61 | assertEquals(`collection from 1 to 2`[1], list[1]) 62 | 63 | assertEquals(`first item`, list[2]) 64 | 65 | list.assertSize(3) 66 | 67 | } 68 | 69 | @Test 70 | open fun `test removeAt`() { 71 | 72 | val list = `get collection to test`() 73 | 74 | assertTrue { list.addAll(`list of 3 different items`) } 75 | 76 | list.assertSize(3) 77 | 78 | assertEquals(`list of 3 different items`[2], list.removeAt(2)) 79 | 80 | list.assertSize(2) 81 | 82 | assertEquals(`list of 3 different items`[1], list.removeAt(1)) 83 | 84 | list.assertSize(1) 85 | 86 | assertEquals(`list of 3 different items`[0], list.removeAt(0)) 87 | 88 | list.assertSize(0) 89 | 90 | assertFailsWith(IndexOutOfBoundsException::class) { 91 | list.removeAt(0) 92 | } 93 | 94 | list.assertSize(0) 95 | 96 | } 97 | 98 | @Test 99 | open fun `test addByIndex`() { 100 | 101 | val list = `get collection to test`() 102 | 103 | assertEquals(Unit, list.add(0, `list of 4 different items`[0])) 104 | 105 | assertEquals(Unit, list.add(0, `list of 4 different items`[1])) 106 | 107 | assertEquals(Unit, list.add(0, `list of 4 different items`[2])) 108 | 109 | list.assertSize(3) 110 | 111 | } 112 | 113 | @Test 114 | open fun `test indexOf`() { 115 | 116 | val list = `get collection to test`() 117 | 118 | assertTrue { list.addAll(`list of 3 different items`.take(2)) } 119 | 120 | assertEquals(0, list.indexOf(`list of 3 different items`[0])) 121 | 122 | assertEquals(1, list.indexOf(`list of 3 different items`[1])) 123 | 124 | assertEquals(-1, list.indexOf(`list of 3 different items`[2])) 125 | 126 | 127 | } 128 | 129 | @Test 130 | open fun `test lastIndexOf`() { 131 | 132 | val list = `get collection to test`() 133 | 134 | assertTrue { list.add(`list of 3 different items`[0]) } 135 | 136 | assertEquals(0, list.lastIndexOf(`list of 3 different items`[0])) 137 | 138 | assertEquals(Unit, list.add(0, `list of 3 different items`[1])) 139 | 140 | assertEquals(1, list.lastIndexOf(`list of 3 different items`[0])) 141 | 142 | assertEquals(-1, list.lastIndexOf(`list of 3 different items`[2])) 143 | 144 | assertTrue { list.remove(`list of 3 different items`[0]) } 145 | 146 | assertEquals(-1, list.lastIndexOf(`list of 3 different items`[0])) 147 | } 148 | 149 | @Test 150 | open fun `test listIterator`() { 151 | 152 | val list = `get collection to test`() 153 | 154 | assertTrue { list.addAll(`list of 3 different items`) } 155 | 156 | val listIterator = list.listIterator() 157 | 158 | list.assertSize(3) 159 | 160 | repeat(3) { 161 | 162 | assertTrue { listIterator.hasNext() } 163 | 164 | assertEquals(`list of 3 different items`[it], listIterator.next()) 165 | 166 | assertEquals(it + 1, listIterator.nextIndex()) 167 | 168 | assertTrue { listIterator.hasPrevious() } 169 | 170 | assertEquals(it, listIterator.previousIndex()) 171 | 172 | assertEquals(`list of 3 different items`[it], listIterator.previous()) 173 | 174 | assertEquals(`list of 3 different items`[it], listIterator.next()) 175 | 176 | } 177 | 178 | list.assertSize(3) 179 | 180 | val `second listIterator` = list.listIterator() 181 | 182 | repeat(3) { 183 | 184 | assertTrue { `second listIterator`.hasNext() } 185 | 186 | assertEquals(`list of 3 different items`[it], `second listIterator`.next()) 187 | 188 | assertEquals(Unit, `second listIterator`.remove()) 189 | 190 | list.assertSize(3 - (it + 1)) 191 | } 192 | 193 | list.assertSize(0) 194 | 195 | val `third listIterator` = list.listIterator() 196 | 197 | repeat(2) { 198 | 199 | `third listIterator`.add(`list of 4 different items`[it]) 200 | } 201 | 202 | list.assertSize(2) 203 | 204 | } 205 | 206 | @Test 207 | open fun `test listIteratorByIndex`() { 208 | 209 | val list = `get collection to test`() 210 | 211 | assertTrue { list.addAll(`list of 3 different items`) } 212 | 213 | val listIterator = list.listIterator(1) 214 | 215 | list.assertSize(3) 216 | 217 | repeat(2) { 218 | 219 | assertTrue { listIterator.hasNext() } 220 | 221 | assertEquals(`list of 3 different items`[it + 1], listIterator.next()) 222 | 223 | assertEquals(it + 1 + 1, listIterator.nextIndex()) 224 | 225 | assertTrue { listIterator.hasPrevious() } 226 | 227 | assertEquals(it + 1, listIterator.previousIndex()) 228 | 229 | assertEquals(`list of 3 different items`[it + 1], listIterator.previous()) 230 | 231 | assertEquals(`list of 3 different items`[it + 1], listIterator.next()) 232 | 233 | } 234 | 235 | list.assertSize(3) 236 | 237 | val `second listIterator` = list.listIterator(1) 238 | 239 | repeat(2) { 240 | 241 | assertTrue { `second listIterator`.hasNext() } 242 | 243 | assertEquals(`list of 3 different items`[it + 1], `second listIterator`.next()) 244 | 245 | assertEquals(Unit, `second listIterator`.remove()) 246 | 247 | list.assertSize(3 - (it + 1)) 248 | } 249 | 250 | list.assertSize(1) 251 | 252 | val `third listIterator` = list.listIterator(1) 253 | 254 | repeat(1) { 255 | 256 | `third listIterator`.add(`list of 4 different items`[it]) 257 | } 258 | 259 | list.assertSize(2) 260 | 261 | } 262 | 263 | @Test 264 | open fun `test subList`() { 265 | 266 | val list = `get collection to test`() 267 | 268 | assertTrue { list.addAll(`list of 3 different items`) } 269 | 270 | val subList = list.subList(1, 3) 271 | 272 | subList.assertSize(2) 273 | 274 | subList.clear() 275 | 276 | subList.assertSize(0) 277 | 278 | list.assertSize(1) 279 | 280 | assertTrue { list.contains(`list of 3 different items`[0]) } 281 | 282 | assertTrue { subList.add(`list of 4 different items`[0]) } 283 | 284 | assertTrue { subList.addAll(`list of 4 different items`.drop(1)) } 285 | 286 | subList.assertSize(4) 287 | 288 | list.assertSize(5) 289 | 290 | } 291 | 292 | @Test 293 | open fun `test set`() { 294 | 295 | val list = `get collection to test`() 296 | 297 | assertTrue { list.addAll(`list of 3 different items`) } 298 | 299 | list.assertSize(3) 300 | 301 | assertEquals(`list of 3 different items`[0], list.set(0, `list of 4 different items`[0])) 302 | 303 | list.assertSize(3) 304 | 305 | assertTrue { list.containsAll(`list of 3 different items`.drop(1) + `list of 4 different items`[0]) } 306 | 307 | assertFailsWith(IndexOutOfBoundsException::class) { 308 | 309 | list[3] = `list of 4 different items`[1] 310 | } 311 | } 312 | 313 | @Test 314 | open fun `test get`() { 315 | 316 | val list = `get collection to test`() 317 | 318 | assertTrue { list.addAll(`list of 3 different items`.take(2)) } 319 | 320 | assertEquals(`list of 3 different items`[0], list[0]) 321 | 322 | assertEquals(`list of 3 different items`[1], list[1]) 323 | 324 | assertFailsWith(IndexOutOfBoundsException::class) { 325 | list[2] 326 | } 327 | 328 | } 329 | 330 | //TODO: implement 331 | override fun `test removeIf`() { 332 | //super.`test removeIf`() 333 | } 334 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/_testutils/templates/MapTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | @file:Suppress("PackageName") 26 | 27 | package io.bluego.powercollections._testutils.templates 28 | 29 | import org.junit.Test 30 | import kotlin.test.* 31 | 32 | @Suppress("RemoveRedundantBackticks", "PropertyName", "LocalVariableName") 33 | abstract class MapTest { 34 | 35 | protected abstract val `map of 3 different items`: LinkedHashMap 36 | 37 | protected abstract val `map of 4 different items`: LinkedHashMap 38 | 39 | protected abstract val `get map to test`: () -> MutableMap 40 | 41 | init { 42 | @Suppress("LeakingThis") 43 | assert(`map of 3 different items`.size == 3) 44 | @Suppress("LeakingThis") 45 | assert(`map of 4 different items`.size == 4) 46 | } 47 | 48 | @Test 49 | open fun `test putAll`() { 50 | 51 | val map = `get map to test`() 52 | 53 | map.putAll(`map of 3 different items`) 54 | 55 | map.assertSize(3) 56 | 57 | } 58 | 59 | @Test 60 | open fun `test size`() { 61 | 62 | val map = `get map to test`() 63 | 64 | map.assertSize(0) 65 | 66 | map.putAll(`map of 3 different items`) 67 | 68 | map.assertSize(3) 69 | } 70 | 71 | @Test 72 | open fun `test remove`() { 73 | 74 | val map = `get map to test`() 75 | 76 | map.putAll(`map of 3 different items`) 77 | 78 | map.assertSize(3) 79 | 80 | assertTrue { map.remove(`map of 3 different items`.at(0)) } 81 | 82 | map.assertSize(2) 83 | 84 | assertTrue { map.remove(`map of 3 different items`.at(1)) } 85 | 86 | map.assertSize(1) 87 | 88 | assertTrue { map.remove(`map of 3 different items`.at(2)) } 89 | 90 | map.assertSize(0) 91 | 92 | assertFalse { map.remove(`map of 3 different items`.at(2)) } 93 | 94 | map.assertSize(0) 95 | 96 | } 97 | 98 | @Test 99 | open fun `test put`() { 100 | 101 | val map = `get map to test`() 102 | 103 | assertNull(map.put(`map of 4 different items`.at(0))) 104 | 105 | assertNull(map.put(`map of 4 different items`.at(1))) 106 | 107 | assertNull(map.put(`map of 4 different items`.at(2))) 108 | 109 | map.assertSize(3) 110 | 111 | } 112 | 113 | @Test 114 | open fun `test containsKey`() { 115 | 116 | val map = `get map to test`() 117 | 118 | map.putAll(`map of 3 different items`.drop(1)) 119 | 120 | assertTrue { map.containsKey(`map of 3 different items`.keyAt(1)) } 121 | 122 | assertTrue { map.containsKey(`map of 3 different items`.keyAt(2)) } 123 | 124 | assertFalse { map.containsKey(`map of 3 different items`.keyAt(0)) } 125 | 126 | } 127 | 128 | @Test 129 | open fun `test containsValue`() { 130 | 131 | val map = `get map to test`() 132 | 133 | map.putAll(`map of 3 different items`.drop(1)) 134 | 135 | assertTrue { map.containsValue(`map of 3 different items`.valueAt(1)) } 136 | 137 | assertTrue { map.containsValue(`map of 3 different items`.valueAt(2)) } 138 | 139 | assertFalse { map.containsValue(`map of 3 different items`.valueAt(0)) } 140 | 141 | } 142 | 143 | @Test 144 | open fun `test get`() { 145 | 146 | val map = `get map to test`() 147 | 148 | map.putAll(`map of 3 different items`.take(2)) 149 | 150 | assertEquals(`map of 3 different items`.valueAt(0), map[`map of 3 different items`.keyAt(0)]) 151 | 152 | assertEquals(`map of 3 different items`.valueAt(1), map[`map of 3 different items`.keyAt(1)]) 153 | 154 | assertNull(map[`map of 3 different items`.keyAt(2)]) 155 | 156 | } 157 | 158 | @Test 159 | open fun `test isEmpty`() { 160 | 161 | val map = `get map to test`() 162 | 163 | map.putAll(`map of 3 different items`) 164 | 165 | assertFalse { map.isEmpty() } 166 | 167 | assertEquals(Unit, map.clear()) 168 | 169 | assertTrue { map.isEmpty() } 170 | 171 | } 172 | 173 | @Test 174 | open fun `test keys`() { 175 | 176 | val map = `get map to test`() 177 | 178 | map.putAll(`map of 3 different items`) 179 | 180 | assertEquals(`map of 3 different items`.keys, map.keys) 181 | 182 | } 183 | 184 | @Test 185 | open fun `test values`() { 186 | 187 | val map = `get map to test`() 188 | 189 | map.putAll(`map of 3 different items`) 190 | 191 | assertEquals(`map of 3 different items`.values.toList(), map.values.toList()) 192 | 193 | } 194 | 195 | @Test 196 | open fun `test entries`() { 197 | 198 | val map = `get map to test`() 199 | 200 | map.putAll(`map of 3 different items`) 201 | 202 | assertEquals(`map of 3 different items`.entries, map.entries) 203 | 204 | } 205 | 206 | @Test 207 | open fun `test mutableEntry`() { 208 | 209 | val map = `get map to test`() 210 | 211 | map.putAll(`map of 3 different items`) 212 | 213 | assertEquals(`map of 3 different items`.entries, map.entries) 214 | 215 | val newValue = `map of 4 different items`.at(0).value 216 | 217 | for (entry in map.entries) { 218 | entry.setValue(newValue) 219 | } 220 | 221 | assertEquals(`map of 3 different items`.entries.map { it.key to newValue }, map.entries.map(Map.Entry::toPair)) 222 | 223 | } 224 | 225 | @Test 226 | open fun `test iterator`() { 227 | 228 | val map = `get map to test`() 229 | 230 | map.putAll(`map of 3 different items`) 231 | 232 | val iterator = map.iterator() 233 | 234 | map.assertSize(3) 235 | 236 | repeat(3) { 237 | 238 | assertTrue { iterator.hasNext() } 239 | 240 | if (it < 3) assertEquals(`map of 3 different items`.at(it), iterator.next()) 241 | else assertFailsWith(ArrayIndexOutOfBoundsException::class) { iterator.next() } 242 | 243 | assertEquals(Unit, iterator.remove()) 244 | 245 | map.assertSize(3 - (it + 1)) 246 | } 247 | 248 | map.assertSize(0) 249 | } 250 | 251 | @Test 252 | open fun `test clear`() { 253 | 254 | val map = `get map to test`() 255 | 256 | map.assertSize(0) 257 | 258 | map.putAll(`map of 3 different items`) 259 | 260 | map.assertSize(3) 261 | 262 | assertEquals(Unit, map.clear()) 263 | 264 | map.assertSize(0) 265 | } 266 | 267 | @Test 268 | open fun `test replace`() { 269 | 270 | val map = `get map to test`() 271 | 272 | map.putAll(`map of 3 different items`) 273 | 274 | val failEntry0 = `map of 4 different items`.at(2) 275 | 276 | val failEntry1 = `map of 4 different items`.at(3) 277 | 278 | val entry0 = `map of 3 different items`.at(0) 279 | 280 | assertTrue(map.replace(entry0.key, entry0.value, `map of 4 different items`.valueAt(0))) 281 | 282 | assertEquals(`map of 3 different items`.apply { replace(`map of 3 different items`.keyAt(0), `map of 4 different items`.valueAt(0)) }, map) 283 | 284 | assertFalse(map.replace(failEntry0.key, failEntry0.value, failEntry1.value)) 285 | 286 | 287 | val entry1 = `map of 3 different items`.at(1) 288 | 289 | assertEquals(`map of 3 different items`.valueAt(1), map.replace(entry1.key, `map of 4 different items`.valueAt(1))) 290 | 291 | assertEquals(`map of 3 different items`.apply { 292 | replace(`map of 3 different items`.keyAt(0), `map of 4 different items`.valueAt(0)) 293 | replace(`map of 3 different items`.keyAt(1), `map of 4 different items`.valueAt(1)) }, map) 294 | 295 | assertNull(map.replace(failEntry0.key, failEntry1.value)) 296 | 297 | } 298 | 299 | @Test 300 | open fun `test putIfAbsent`() { 301 | 302 | val map = `get map to test`() 303 | 304 | val failEntry0 = `map of 4 different items`.at(3) 305 | 306 | map.putAll(`map of 3 different items`) 307 | 308 | map.assertSize(3) 309 | 310 | assertEquals(`map of 3 different items`.valueAt(0), map.putIfAbsent(`map of 3 different items`.keyAt(0), failEntry0.value)) 311 | 312 | map.assertSize(3) 313 | 314 | 315 | val newEntry = `map of 4 different items`.at(0) 316 | 317 | assertNull(map.putIfAbsent(newEntry.key, newEntry.value)) 318 | 319 | map.assertSize(4) 320 | 321 | assertEquals(`map of 3 different items` + `map of 4 different items`.take(1), map) 322 | 323 | } 324 | 325 | protected fun Map<*, *>.assertSize(size: Int) { 326 | assertEquals(size, this.size) 327 | } 328 | 329 | protected fun MutableMap.remove(entry: Map.Entry): Boolean { 330 | return this.remove(entry.key, entry.value) 331 | } 332 | 333 | protected fun LinkedHashMap.at(index: Int): Map.Entry { 334 | return this.asIterable().elementAtOrNull(index) ?: throw IndexOutOfBoundsException() 335 | } 336 | 337 | protected fun LinkedHashMap.keyAt(index: Int): K { 338 | return this.at(index).key 339 | } 340 | 341 | protected fun LinkedHashMap.valueAt(index: Int): V { 342 | return this.at(index).value 343 | } 344 | 345 | protected fun LinkedHashMap.removeAt(index: Int): V { 346 | return this.remove(this.at(index).key) ?: throw NoSuchElementException() 347 | } 348 | 349 | protected fun MutableMap.put(entry: Map.Entry): V? { 350 | return this.put(entry.key, entry.value) 351 | } 352 | 353 | protected fun LinkedHashMap.drop(number: Int): Map { 354 | return this.asIterable().drop(number).associate(Map.Entry::toPair) 355 | } 356 | 357 | protected fun LinkedHashMap.take(number: Int): Map { 358 | return this.asIterable().take(number).associate(Map.Entry::toPair) 359 | } 360 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/_testutils/templates/SetTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections._testutils.templates 26 | 27 | import io.bluego.powercollections._testutils.assertSize 28 | import org.junit.Test 29 | import kotlin.test.assertEquals 30 | import kotlin.test.assertFalse 31 | import kotlin.test.assertTrue 32 | 33 | @Suppress("PropertyName") 34 | abstract class SetTest: CollectionTest() { 35 | 36 | abstract override val `list of 3 different items`: List 37 | 38 | abstract override val `list of 4 different items`: List 39 | 40 | abstract override val `get collection to test`: () -> MutableSet 41 | 42 | 43 | @Test 44 | open fun `test add same item`() { 45 | 46 | val set = `get collection to test`() 47 | 48 | assertTrue(set.add(`list of 3 different items`[0])) 49 | 50 | set.assertSize(1) 51 | 52 | assertFalse(set.add(`list of 3 different items`[0])) 53 | 54 | set.assertSize(1) 55 | 56 | assertEquals(`list of 3 different items`.take(1), set.toList()) 57 | } 58 | 59 | @Test 60 | open fun `test addAll same item`() { 61 | 62 | val set = `get collection to test`() 63 | 64 | assertTrue(set.addAll(`list of 3 different items`)) 65 | 66 | set.assertSize(3) 67 | 68 | assertFalse(set.addAll(`list of 3 different items`)) 69 | 70 | set.assertSize(3) 71 | 72 | assertEquals(`list of 3 different items`, set.toList()) 73 | 74 | } 75 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/bounded/BoundedMapTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.bounded 26 | 27 | import io.bluego.powercollections._testutils.templates.MapTest 28 | import org.junit.Test 29 | import kotlin.test.* 30 | 31 | @Suppress("RemoveRedundantBackticks", "PropertyName", "LocalVariableName") 32 | open class BoundedMapTest: MapTest() { 33 | 34 | override val `map of 3 different items`: LinkedHashMap 35 | get() = linkedMapOf(0 to "zero", 1 to "one", 2 to "two") 36 | 37 | override val `map of 4 different items`: LinkedHashMap 38 | get() = linkedMapOf(10 to "ten", 11 to "eleven", 12 to "twelve", 13 to "thirteen") 39 | 40 | override val `get map to test`: () -> MutableBoundedMap 41 | get() = { mutableBoundedMapOf(3) } 42 | 43 | @Test 44 | override fun `test putAll`() { 45 | 46 | val `first BoundedMap` = `get map to test`() 47 | 48 | `first BoundedMap`.putAll(`map of 3 different items`) 49 | 50 | `first BoundedMap`.assertSize(3) 51 | 52 | assertFailsWith(IndexOutOfBoundsException::class) { 53 | `first BoundedMap`.putAll(`map of 4 different items`) 54 | } 55 | 56 | `first BoundedMap`.assertSize(3) 57 | 58 | /* _______________________________________________________________ */ 59 | 60 | val `second BoundedMap` = `get map to test`() 61 | 62 | assertFailsWith(IndexOutOfBoundsException::class) { 63 | `second BoundedMap`.putAll(`map of 4 different items`) 64 | } 65 | 66 | /* 67 | IMPORTANT 68 | putAll first puts all items in the map and throws an error afterwards, 69 | if the additional entries exceed the maxSize 70 | */ 71 | `second BoundedMap`.assertSize(3) 72 | 73 | } 74 | 75 | @Test 76 | open fun `test forceResize`() { 77 | 78 | val `first boundedMap` = `get map to test`() 79 | 80 | assertFalse { `first boundedMap`.forceResize(1) } 81 | 82 | assertNull(`first boundedMap`.put(`map of 3 different items`.at(0))) 83 | 84 | `first boundedMap`.assertSize(1) 85 | 86 | assertFailsWith(IndexOutOfBoundsException::class) { 87 | `first boundedMap`.put(`map of 3 different items`.at(1)) 88 | } 89 | 90 | `first boundedMap`.assertSize(1) 91 | 92 | 93 | val `second boundedMap` = `get map to test`() 94 | 95 | `second boundedMap`.putAll(`map of 3 different items`) 96 | 97 | `second boundedMap`.assertSize(3) 98 | 99 | assertTrue { `second boundedMap`.forceResize(1) } 100 | 101 | `second boundedMap`.assertSize(1) 102 | 103 | assertEquals(`map of 3 different items`.valueAt(2), `second boundedMap`[`map of 3 different items`.keyAt(2)]) 104 | 105 | assertFailsWith(IndexOutOfBoundsException::class) { 106 | `second boundedMap`.put(`map of 3 different items`.at(1)) 107 | } 108 | 109 | `second boundedMap`.assertSize(1) 110 | 111 | } 112 | 113 | @Test 114 | override fun `test put`() { 115 | 116 | val boundedList = `get map to test`() 117 | 118 | assertNull(boundedList.put(`map of 4 different items`.at(0))) 119 | 120 | assertNull(boundedList.put(`map of 4 different items`.at(1))) 121 | 122 | assertNull(boundedList.put(`map of 4 different items`.at(2))) 123 | 124 | boundedList.assertSize(3) 125 | 126 | assertFailsWith(IndexOutOfBoundsException::class) { 127 | boundedList.put(`map of 4 different items`.at(3)) 128 | } 129 | 130 | boundedList.assertSize(3) 131 | } 132 | 133 | @Test 134 | open fun `test forcePut`() { 135 | 136 | val boundedList = `get map to test`() 137 | 138 | assertFalse { boundedList.forcePut(`map of 4 different items`.at(0)) } 139 | 140 | assertFalse { boundedList.forcePut(`map of 4 different items`.at(1)) } 141 | 142 | assertFalse { boundedList.forcePut(`map of 4 different items`.at(2)) } 143 | 144 | boundedList.assertSize(3) 145 | 146 | assertTrue { boundedList.forcePut(`map of 4 different items`.at(3)) } 147 | 148 | boundedList.assertSize(3) 149 | 150 | assertTrue { boundedList.containsAll(`map of 4 different items`.drop(1)) } 151 | 152 | } 153 | 154 | @Test 155 | override fun `test iterator`() { 156 | 157 | val boundedList = `get map to test`() 158 | 159 | boundedList.putAll(`map of 3 different items`) 160 | 161 | val iterator = boundedList.iterator() 162 | 163 | boundedList.assertSize(3) 164 | 165 | repeat(3) { 166 | 167 | assertTrue { iterator.hasNext() } 168 | 169 | if (it < 3) assertEquals(`map of 3 different items`.at(it), iterator.next()) 170 | else assertFailsWith(ArrayIndexOutOfBoundsException::class) { iterator.next() } 171 | 172 | assertEquals(Unit, iterator.remove()) 173 | 174 | boundedList.assertSize(3 - (it + 1)) 175 | } 176 | 177 | boundedList.assertSize(0) 178 | } 179 | 180 | @Test 181 | open fun `test maxCapacity`() { 182 | 183 | val boundedList = `get map to test`() 184 | 185 | assertEquals(3, boundedList.maxCapacity) 186 | 187 | assertEquals(Unit, boundedList.resize(1)) 188 | 189 | assertEquals(1, boundedList.maxCapacity) 190 | 191 | assertFalse { boundedList.forceResize(4) } 192 | 193 | assertEquals(4, boundedList.maxCapacity) 194 | } 195 | 196 | @Test 197 | open fun `test forcePutAll`() { 198 | 199 | val boundedList = `get map to test`() 200 | 201 | boundedList.assertSize(0) 202 | 203 | assertTrue { boundedList.forcePutAll(`map of 4 different items`) } 204 | 205 | boundedList.assertSize(3) 206 | 207 | assertTrue { boundedList.forcePutAll(`map of 4 different items`.take(3)) } 208 | } 209 | 210 | @Test 211 | open fun `test resize`() { 212 | 213 | val boundedList = `get map to test`() 214 | 215 | assertEquals(3, boundedList.maxCapacity) 216 | 217 | assertEquals(Unit, boundedList.resize(5)) 218 | 219 | assertEquals(5, boundedList.maxCapacity) 220 | 221 | assertEquals(Unit, boundedList.resize(2)) 222 | 223 | assertEquals(2, boundedList.maxCapacity) 224 | 225 | boundedList.assertSize(0) 226 | 227 | boundedList.putAll(`map of 3 different items`.take(2)) 228 | 229 | boundedList.assertSize(2) 230 | 231 | assertEquals(Unit, boundedList.resize(3)) 232 | 233 | assertNull(boundedList.put(`map of 3 different items`.at(2))) 234 | 235 | assertFailsWith(IndexOutOfBoundsException::class) { 236 | boundedList.put(`map of 4 different items`.at(0)) 237 | } 238 | 239 | assertFailsWith(IllegalStateException::class) { 240 | boundedList.resize(2) 241 | } 242 | 243 | } 244 | 245 | override fun `test putIfAbsent`() { 246 | 247 | val map = `get map to test`() 248 | 249 | map.putAll(`map of 3 different items`) 250 | 251 | map.assertSize(3) 252 | 253 | assertEquals(`map of 3 different items`.valueAt(0), map.putIfAbsent(`map of 3 different items`.keyAt(0), "failure")) 254 | 255 | map.assertSize(3) 256 | 257 | 258 | val newEntry = `map of 4 different items`.at(0) 259 | 260 | assertFailsWith(IndexOutOfBoundsException::class) { 261 | map.putIfAbsent(newEntry.key, newEntry.value) 262 | } 263 | 264 | map.assertSize(3) 265 | 266 | assertEquals>(`map of 3 different items`, map) 267 | 268 | } 269 | 270 | protected fun MutableBoundedMap.forcePut(entry: Map.Entry): Boolean { 271 | return this.forcePut(entry.key, entry.value) 272 | } 273 | 274 | protected fun Map.containsAll(map: Map): Boolean { 275 | return this.entries.containsAll(map.entries) 276 | } 277 | 278 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/observable/AbstractDummyObserver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable 26 | 27 | import kotlin.test.assertEquals 28 | 29 | @Suppress("PropertyName") 30 | abstract class AbstractDummyObserver(val isDebug: Boolean = false) { 31 | 32 | protected val mAddedList = mutableListOf>() 33 | protected val mRemovedList = mutableListOf>() 34 | protected val mReplacedList = mutableListOf>>() 35 | 36 | fun reset() { 37 | mRemovedList.clear() 38 | mAddedList.clear() 39 | mReplacedList.clear() 40 | } 41 | 42 | inline fun assertWasAdded(number: Int, action: () -> Unit) { 43 | val start = `access$mAddedList`.size 44 | action() 45 | assertEquals(number, `access$mAddedList`.size - start, if (isDebug) "assertWasAdded" else null) 46 | } 47 | 48 | inline fun assertWasRemoved(number: Int, action: () -> Unit) { 49 | val start = `access$mRemovedList`.size 50 | action() 51 | assertEquals(number, `access$mRemovedList`.size - start, if (isDebug) "assertWasRemoved" else null) 52 | } 53 | 54 | inline fun assertWasReplaced(number: Int, action: () -> Unit) { 55 | val start = `access$mReplacedList`.size 56 | action() 57 | assertEquals(number, `access$mReplacedList`.size - start, if (isDebug) "AssertWasReplaced" else null) 58 | } 59 | 60 | inline fun assertWas(added: Int, removed: Int, replaced: Int, action: () -> Unit) { 61 | assertWasAdded(added) { 62 | assertWasRemoved(removed) { 63 | assertWasReplaced(replaced, action) 64 | } 65 | } 66 | } 67 | 68 | @PublishedApi 69 | internal val `access$mAddedList`: MutableList> 70 | get() = mAddedList 71 | @PublishedApi 72 | internal val `access$mRemovedList`: MutableList> 73 | get() = mRemovedList 74 | @PublishedApi 75 | internal val `access$mReplacedList`: MutableList>> 76 | get() = mReplacedList 77 | 78 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/observable/list/DummyListObserver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.list 26 | 27 | import io.bluego.powercollections.observable.AbstractDummyObserver 28 | 29 | @Suppress("ConstantConditionIf") 30 | class DummyListObserver : AbstractDummyObserver, ListObserver { 31 | 32 | constructor() : super() 33 | constructor(isDebug: Boolean) : super(isDebug) 34 | 35 | override fun wasAdded(index: Int, element: E) { 36 | if (isDebug) println("Added: index: $index element: $element") 37 | mAddedList.add(index to element) 38 | } 39 | 40 | override fun wasAdded(elements: Map) { 41 | if (isDebug) println("Added: $elements") 42 | mAddedList.addAll(elements.toList()) 43 | } 44 | 45 | override fun wasRemoved(index: Int, element: E) { 46 | if (isDebug) println("Removed: index: $index element: $element") 47 | mRemovedList.add(index to element) 48 | } 49 | 50 | override fun wasRemoved(elements: Map) { 51 | if (isDebug) println("Removed: $elements") 52 | mRemovedList.addAll(elements.toList()) 53 | } 54 | 55 | override fun wasReplaced(index: Int, lastElement: E, newElement: E) { 56 | if (isDebug) println("Replaced $index: $lastElement with $newElement") 57 | mReplacedList.add(index to (lastElement to newElement)) 58 | } 59 | 60 | override fun wasReplaced(map: Map>) { 61 | if (isDebug) println("Replaced: $map") 62 | mReplacedList.addAll(map.toList()) 63 | } 64 | 65 | override fun notifyDataChanged() { 66 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 67 | } 68 | 69 | override fun notifyDataChanged(index: Int) { 70 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/observable/list/ObservableListTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.list 26 | 27 | import io.bluego.powercollections._testutils.templates.ListTest 28 | import kotlin.test.Test 29 | import kotlin.test.assertEquals 30 | 31 | @Suppress("RemoveRedundantBackticks", "PropertyName") 32 | open class ObservableListTest : ListTest() { 33 | 34 | override val `list of 3 different items`: List 35 | get() = listOf("zero", "one", "two") 36 | 37 | override val `list of 4 different items`: List 38 | get() = listOf("_zero", "_one", "_two", "_three") 39 | 40 | override val `get collection to test`: () -> MutableList 41 | get() = { mutableObservableListOf(mDummyObserver) } 42 | 43 | private val mDummyObserver = DummyListObserver() 44 | 45 | 46 | override fun `test add`() { 47 | mDummyObserver.assertWas(3, 0, 0) { 48 | super.`test add`() 49 | } 50 | } 51 | 52 | override fun `test addAll`() { 53 | mDummyObserver.assertWas(3, 0, 0) { 54 | super.`test addAll`() 55 | } 56 | } 57 | 58 | override fun `test addAllByIndex`() { 59 | mDummyObserver.assertWas(3, 0, 0) { 60 | super.`test addAllByIndex`() 61 | } 62 | } 63 | 64 | override fun `test addByIndex`() { 65 | mDummyObserver.assertWas(3, 0, 0) { 66 | super.`test addByIndex`() 67 | } 68 | } 69 | 70 | override fun `test iterator`() { 71 | mDummyObserver.assertWas(3, 3, 0) { 72 | super.`test iterator`() 73 | } 74 | } 75 | 76 | override fun `test listIterator`() { 77 | mDummyObserver.assertWas(5, 3, 0) { 78 | super.`test listIterator`() 79 | } 80 | } 81 | 82 | @Test 83 | fun `test listIterator set add`() { 84 | mDummyObserver.assertWas(4, 0, 3) { 85 | 86 | val list = `get collection to test`() 87 | 88 | list.addAll(`list of 3 different items`) 89 | 90 | list.listIterator().let { 91 | var counter = 0 92 | while (it.hasNext()) { 93 | it.next() 94 | it.set(`list of 4 different items`[counter]) 95 | counter++ 96 | } 97 | 98 | it.add(`list of 4 different items`.last()) 99 | } 100 | 101 | assertEquals(`list of 4 different items`, list) 102 | } 103 | } 104 | 105 | override fun `test listIteratorByIndex`() { 106 | mDummyObserver.assertWas(4, 2, 0) { 107 | super.`test listIteratorByIndex`() 108 | } 109 | } 110 | 111 | override fun `test remove`() { 112 | mDummyObserver.assertWas(3, 3, 0) { 113 | super.`test remove`() 114 | } 115 | } 116 | 117 | override fun `test removeAt`() { 118 | mDummyObserver.assertWas(3, 3, 0) { 119 | super.`test removeAt`() 120 | } 121 | } 122 | 123 | override fun `test clear`() { 124 | mDummyObserver.assertWas(3, 3, 0) { 125 | super.`test clear`() 126 | } 127 | } 128 | 129 | override fun `test retainAll`() { 130 | mDummyObserver.assertWas(3, 2, 0) { 131 | super.`test retainAll`() 132 | } 133 | } 134 | 135 | override fun `test removeAll`() { 136 | mDummyObserver.assertWas(5, 3, 0) { 137 | super.`test removeAll`() 138 | } 139 | } 140 | 141 | override fun `test set`() { 142 | mDummyObserver.assertWas(3, 0, 1) { 143 | super.`test set`() 144 | } 145 | } 146 | 147 | override fun `test subList`() { 148 | mDummyObserver.assertWas(7, 2, 0) { 149 | super.`test subList`() 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/observable/map/DummyMapObserver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.map 26 | 27 | import io.bluego.powercollections.observable.AbstractDummyObserver 28 | 29 | 30 | class DummyMapObserver : AbstractDummyObserver, MapObserver { 31 | 32 | constructor() : super() 33 | constructor(isDebug: Boolean) : super(isDebug) 34 | 35 | override fun wasAdded(key: K, value: V) { 36 | if (isDebug) println("Added: key: $key value: $value") 37 | mAddedList.add(key to value) 38 | } 39 | 40 | override fun wasAdded(entries: Map) { 41 | if (isDebug) println("Added: $entries") 42 | mAddedList.addAll(entries.map(Map.Entry::toPair)) 43 | } 44 | 45 | override fun wasRemoved(key: K, value: V) { 46 | if (isDebug) println("Removed: key: $key value: $value") 47 | mRemovedList.add(key to value) 48 | } 49 | 50 | override fun wasRemoved(entries: Map) { 51 | if (isDebug) println("Removed: $entries") 52 | mRemovedList.addAll(entries.map(Map.Entry::toPair)) 53 | } 54 | 55 | override fun wasReplaced(key: K, lastValue: V, newValue: V) { 56 | if (isDebug) println("Replaced key: $key lastValue: $lastValue with newValue $newValue") 57 | mReplacedList.add(key to (lastValue to newValue)) 58 | } 59 | 60 | override fun wasReplaced(entries: Map>) { 61 | if (isDebug) println("Replaced: $entries") 62 | mReplacedList.addAll(entries.map(Map.Entry>::toPair)) 63 | } 64 | 65 | override fun notifyDataChanged() { 66 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 67 | } 68 | 69 | override fun notifyDataChanged(key: K) { 70 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 71 | } 72 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/observable/map/ObservableMapTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.observable.map 26 | 27 | import io.bluego.powercollections._testutils.templates.MapTest 28 | import org.junit.Test 29 | import kotlin.test.assertNull 30 | import kotlin.test.assertTrue 31 | 32 | @Suppress("PropertyName") 33 | class ObservableMapTest : MapTest() { 34 | 35 | private val mDummyObserver = DummyMapObserver() 36 | 37 | override val `map of 3 different items`: LinkedHashMap 38 | get() = linkedMapOf(0 to "zero", 1 to "one", 2 to "two") 39 | 40 | override val `map of 4 different items`: LinkedHashMap 41 | get() = linkedMapOf(10 to "ten", 11 to "eleven", 12 to "twelve", 13 to "thirteen") 42 | 43 | override val `get map to test`: () -> MutableObservableMap 44 | get() = { mutableObservableMapOf(mDummyObserver) } 45 | 46 | 47 | override fun `test putAll`() { 48 | mDummyObserver.assertWas(3, 0, 0) { 49 | super.`test putAll`() 50 | } 51 | } 52 | 53 | override fun `test remove`() { 54 | mDummyObserver.assertWas(3, 3, 0) { 55 | super.`test remove`() 56 | } 57 | } 58 | 59 | override fun `test put`() { 60 | mDummyObserver.assertWas(3, 0, 0) { 61 | super.`test put`() 62 | } 63 | } 64 | 65 | override fun `test mutableEntry`() { 66 | mDummyObserver.assertWas(3, 0, 3) { 67 | super.`test mutableEntry`() 68 | } 69 | } 70 | 71 | override fun `test iterator`() { 72 | mDummyObserver.assertWas(3, 3, 0) { 73 | super.`test iterator`() 74 | } 75 | } 76 | 77 | override fun `test clear`() { 78 | mDummyObserver.assertWas(3, 3, 0) { 79 | super.`test clear`() 80 | } 81 | } 82 | 83 | override fun `test replace`() { 84 | mDummyObserver.assertWas(3, 0, 2) { 85 | super.`test replace`() 86 | } 87 | } 88 | 89 | override fun `test putIfAbsent`() { 90 | mDummyObserver.assertWas(4, 0, 0) { 91 | super.`test putIfAbsent`() 92 | } 93 | } 94 | 95 | @Test 96 | fun `test remove mutableEntry from entries`() { 97 | 98 | mDummyObserver.assertWas(3, 1, 0) { 99 | 100 | val map = `get map to test`() 101 | 102 | map.putAll(`map of 3 different items`) 103 | 104 | map.assertSize(3) 105 | 106 | map.entries.remove(`map of 3 different items`.at(0)) 107 | 108 | map.assertSize(2) 109 | 110 | } 111 | } 112 | 113 | @Test 114 | fun `test replace from nullable value`() { 115 | 116 | val dummyObserver = DummyMapObserver() 117 | 118 | dummyObserver.assertWas(3, 0, 2) { 119 | 120 | val nullableMap = mutableObservableMapOf(dummyObserver, 121 | 0 to null, 122 | 1 to null, 123 | 2 to "two" 124 | ) 125 | 126 | assertTrue(nullableMap.replace(0, null, "zero")) 127 | 128 | assertNull(nullableMap.replace(1, "one")) 129 | 130 | } 131 | } 132 | 133 | @Test 134 | fun `test putIfAbsent from nullable value`() { 135 | 136 | val dummyObserver = DummyMapObserver() 137 | 138 | dummyObserver.assertWas(3, 0, 1) { 139 | 140 | val nullableMap = mutableObservableMapOf(dummyObserver, 141 | 0 to null, 142 | 1 to null, 143 | 2 to "two" 144 | ) 145 | 146 | assertNull(nullableMap.putIfAbsent(0, "zero")) 147 | 148 | } 149 | 150 | } 151 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/weak/DummyClass.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak 26 | 27 | 28 | data class DummyClass(val tag: String) -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/weak/WeakCollectionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak 26 | 27 | import io.bluego.powercollections._testutils.templates.CollectionTest 28 | import io.bluego.powercollections._testutils.assertSize 29 | import org.junit.Test 30 | import kotlin.test.assertEquals 31 | 32 | @Suppress("PropertyName") 33 | open class WeakCollectionTest: CollectionTest() { 34 | 35 | override val `list of 3 different items`: List 36 | get() = listOf("zero", "one", "two") 37 | 38 | override val `list of 4 different items`: List 39 | get() = listOf("_zero", "_one", "_two", "_three") 40 | 41 | override val `get collection to test`: () -> MutableWeakCollection 42 | get() = { mutableWeakCollectionOf() } 43 | 44 | override fun `test addAll`() { 45 | super.`test addAll`() 46 | } 47 | 48 | @Test 49 | fun `test addAll gc`() { 50 | 51 | val collection = mutableWeakCollectionOf() 52 | 53 | val persistentClasses = listOf(DummyClass("persistent_0"), DummyClass("persistent_1")) 54 | 55 | val notPersistentClasses = { listOf(DummyClass("not_persistent_0"), DummyClass("not_persistent_1")) } 56 | 57 | collection.addAll(persistentClasses) 58 | 59 | collection.addAll(notPersistentClasses()) 60 | 61 | collection.assertSize(4) 62 | 63 | assertEquals(persistentClasses + notPersistentClasses(), collection.toList()) 64 | 65 | System.gc() 66 | 67 | collection.forceOptimize() 68 | 69 | assertEquals(persistentClasses, collection.toList()) 70 | 71 | } 72 | 73 | override fun `test add`() { 74 | super.`test add`() 75 | } 76 | 77 | @Test 78 | fun `test add gc`() { 79 | 80 | val collection = mutableWeakCollectionOf() 81 | 82 | val persistentClass = DummyClass("persistent") 83 | 84 | val notPersistentClass = { DummyClass("not_persistent") } 85 | 86 | collection.add(notPersistentClass()) 87 | 88 | collection.add(persistentClass) 89 | 90 | collection.assertSize(2) 91 | 92 | assertEquals(listOf(notPersistentClass(), persistentClass), collection.toList()) 93 | 94 | System.gc() 95 | 96 | collection.forceOptimize() 97 | 98 | assertEquals(listOf(persistentClass), collection.toList()) 99 | } 100 | 101 | override fun `test iterator`() { 102 | super.`test iterator`() 103 | } 104 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/bluego/powercollections/weak/WeakSetTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Felix Pröhl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package io.bluego.powercollections.weak 26 | 27 | import io.bluego.powercollections._testutils.templates.SetTest 28 | import io.bluego.powercollections._testutils.assertSize 29 | import org.junit.Test 30 | import kotlin.test.assertEquals 31 | 32 | @Suppress("PropertyName") 33 | class WeakSetTest: SetTest() { 34 | 35 | override val `list of 3 different items`: List 36 | get() = listOf("zero", "one", "two") 37 | 38 | override val `list of 4 different items`: List 39 | get() = listOf("_zero", "_one", "_two", "_three") 40 | 41 | override val `get collection to test`: () -> MutableWeakSet 42 | get() = { mutableWeakSetOf() } 43 | 44 | override fun `test addAll`() { 45 | super.`test addAll`() 46 | } 47 | 48 | @Test 49 | fun `test addAll gc`() { 50 | 51 | val collection = mutableWeakCollectionOf() 52 | 53 | val persistentClasses = listOf(DummyClass("persistent_0"), DummyClass("persistent_1")) 54 | 55 | val notPersistentClasses = { listOf(DummyClass("not_persistent_0"), DummyClass("not_persistent_1")) } 56 | 57 | collection.addAll(persistentClasses) 58 | 59 | collection.addAll(notPersistentClasses()) 60 | 61 | collection.assertSize(4) 62 | 63 | assertEquals(persistentClasses + notPersistentClasses(), collection.toList()) 64 | 65 | System.gc() 66 | 67 | collection.forceOptimize() 68 | 69 | assertEquals(persistentClasses, collection.toList()) 70 | 71 | } 72 | 73 | override fun `test add`() { 74 | super.`test add`() 75 | } 76 | 77 | @Test 78 | fun `test add gc`() { 79 | 80 | val collection = mutableWeakCollectionOf() 81 | 82 | val persistentClass = DummyClass("persistent") 83 | 84 | val notPersistentClass = { DummyClass("not_persistent") } 85 | 86 | collection.add(notPersistentClass()) 87 | 88 | collection.add(persistentClass) 89 | 90 | collection.assertSize(2) 91 | 92 | assertEquals(listOf(notPersistentClass(), persistentClass), collection.toList()) 93 | 94 | System.gc() 95 | 96 | collection.forceOptimize() 97 | 98 | assertEquals(listOf(persistentClass), collection.toList()) 99 | } 100 | 101 | override fun `test iterator`() { 102 | super.`test iterator`() 103 | } 104 | } --------------------------------------------------------------------------------