├── settings.gradle ├── .gitignore ├── gradle.properties ├── secring.gpg.enc ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── production.gradle ├── release.sh ├── src ├── test │ └── java │ │ └── com │ │ └── worksap │ │ └── icefig │ │ └── lang │ │ ├── Helpers.java │ │ ├── RangeTest.java │ │ ├── CharSeqTest.java │ │ └── HashTest.java └── main │ └── java │ └── com │ └── worksap │ └── icefig │ └── lang │ ├── Hashes.java │ ├── Seqs.java │ ├── MutableHash.java │ ├── MutableSeq.java │ ├── HashImpl.java │ ├── Range.java │ ├── Hash.java │ ├── SeqImpl.java │ ├── CharSeq.java │ └── Seq.java ├── gradlew.bat ├── .travis.yml ├── README.md ├── gradlew └── LICENSE /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'icefig' 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | .idea/ 4 | *.iml 5 | 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #Wed, 28 Jun 2017 12:41:15 +0900 2 | version=0.2.6-SNAPSHOT 3 | -------------------------------------------------------------------------------- /secring.gpg.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorksApplications/icefig/HEAD/secring.gpg.enc -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorksApplications/icefig/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 27 14:43:25 CST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-bin.zip 7 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./gradlew uploadArchives -PossrhUsername="${SONATYPE_USERNAME}" -PossrhPassword="${SONATYPE_PASSWORD}" -Psigning.keyId="${SIGNING_KEYID}" -Psigning.password="${SIGNING_PASSWORD}" -Psigning.secretKeyRingFile=secring.gpg -Penv=production 4 | 5 | ./gradlew closeAndReleaseRepository -PossrhUsername="${SONATYPE_USERNAME}" -PossrhPassword="${SONATYPE_PASSWORD}" -Penv=production 6 | -------------------------------------------------------------------------------- /src/test/java/com/worksap/icefig/lang/Helpers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | /** 20 | * Created by liuyang on 7/30/15. 21 | */ 22 | public class Helpers { 23 | 24 | public static void assertThrows(Class exception, Runnable action) { 25 | try { 26 | action.run(); 27 | throw new AssertionError("Expected exception: " + exception.getTypeName() + ", but no exception thrown"); 28 | } catch (Exception e) { 29 | if (!e.getClass().equals(exception)) { 30 | throw new AssertionError("Expected exception: " + exception.getTypeName() + ", but was " + e.getClass().getTypeName()); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/Hashes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.Map; 20 | 21 | /** 22 | * Factory class for construct Hash and MutableHash 23 | */ 24 | public class Hashes { 25 | private Hashes() { 26 | 27 | } 28 | 29 | public static Hash newHash() { 30 | return new HashImpl<>(); 31 | } 32 | 33 | public static Hash newHash(Map map) { 34 | return new HashImpl<>(map); 35 | } 36 | 37 | public static MutableHash newMutableHash() { 38 | return new HashImpl<>(); 39 | } 40 | 41 | public static MutableHash newMutableHash(Map map) { 42 | return new HashImpl<>(map); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /gradle/production.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: 'maven' 3 | apply plugin: 'signing' 4 | 5 | task javadocJar(type: Jar, dependsOn: javadoc) { 6 | classifier = 'javadoc' 7 | from tasks.javadoc.destinationDir 8 | } 9 | 10 | task sourcesJar(type: Jar) { 11 | from sourceSets.main.allSource 12 | classifier = 'sources' 13 | } 14 | 15 | artifacts { 16 | archives jar 17 | 18 | archives javadocJar 19 | archives sourcesJar 20 | } 21 | 22 | signing { 23 | sign configurations.archives 24 | } 25 | 26 | def ossrhUsername = hasProperty('ossrhUsername')? ossrhUsername : System.getenv('ossrhUsername') 27 | def ossrhPassword = hasProperty('ossrhPassword')? ossrhPassword : System.getenv('ossrhPassword') 28 | 29 | nexusStaging { 30 | packageGroup = "com.worksap" //optional if packageGroup == project.getGroup() 31 | username = ossrhUsername 32 | password = ossrhPassword 33 | } 34 | 35 | uploadArchives { 36 | repositories { 37 | mavenDeployer { 38 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 39 | 40 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 41 | authentication(userName: ossrhUsername, password: ossrhPassword) 42 | } 43 | 44 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 45 | authentication(userName: ossrhUsername, password: ossrhPassword) 46 | } 47 | 48 | pom.project { 49 | name 'Fig' 50 | packaging 'jar' 51 | // optionally artifactId can be defined here 52 | description 'Java lambda supplement' 53 | url 'http://worksapplications.github.io/icefig/' 54 | 55 | scm { 56 | connection 'scm:git:git@github.com:WorksApplications/icefig.git' 57 | developerConnection 'scm:git:git@github.com:WorksApplications/icefig.git' 58 | url 'git@github.com:WorksApplications/icefig.git' 59 | } 60 | 61 | licenses { 62 | license { 63 | name 'The Apache License, Version 2.0' 64 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 65 | } 66 | } 67 | 68 | developers { 69 | developer { 70 | id 'liuyang' 71 | name 'Liu Yang' 72 | email 'liu_yang@worksap.co.jp' 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/Seqs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.Collection; 22 | 23 | /** 24 | * Factory class for construct Seq and MutableSeq 25 | */ 26 | public class Seqs { 27 | 28 | /** 29 | * Create an empty Seq 30 | */ 31 | public static Seq newSeq() { 32 | return new SeqImpl<>(); 33 | } 34 | 35 | /** 36 | * Create an Seq with the single value 37 | */ 38 | public static Seq newSeq(T value) { 39 | Collection collection = new ArrayList<>(); 40 | collection.add(value); 41 | return new SeqImpl<>(collection); 42 | } 43 | 44 | /** 45 | * Create an Seq with the values 46 | */ 47 | @SuppressWarnings({"varargs", "unchecked"}) 48 | public static Seq newSeq(T... values) { 49 | return new SeqImpl<>(Arrays.asList(values)); 50 | } 51 | 52 | /** 53 | * Create an Seq with the single values inside the collection 54 | */ 55 | public static Seq newSeq(Collection values) { 56 | return new SeqImpl<>(values); 57 | } 58 | 59 | /** 60 | * Create an empty MutableSeq 61 | */ 62 | public static MutableSeq newMutableSeq() { 63 | return new SeqImpl<>(); 64 | } 65 | 66 | /** 67 | * Create an MutableSeq with the single value 68 | */ 69 | public static MutableSeq newMutableSeq(T value) { 70 | Collection collection = new ArrayList<>(); 71 | collection.add(value); 72 | return new SeqImpl<>(collection); 73 | } 74 | 75 | /** 76 | * Create an MutableSeq with the values 77 | */ 78 | @SuppressWarnings({"varargs", "unchecked"}) 79 | public static MutableSeq newMutableSeq(T... values) { 80 | return new SeqImpl<>(Arrays.asList(values)); 81 | } 82 | 83 | /** 84 | * Create an MutableSeq with the single values inside the collection 85 | */ 86 | public static MutableSeq newMutableSeq(Collection values) { 87 | return new SeqImpl<>(values); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | after_success: 5 | - bash <(curl -s https://codecov.io/bash) 6 | env: 7 | global: 8 | - secure: XUdpfc6ewJ0awirYD6fyVVMhwbCUbkkNbTu+qmJEqhgfkf0Iqy0BlSFpfwcYC3xAPfYI7pIf+tw0Fed0w6lPwNETIcoxd4Gm7nn1fUbaicef28gyN9wQK8ybvw0XirYYIQaO0yJC+7TQ9fqXg7yR+csG2EOVZDOb8mUyCL+ZVxgxF2wDzwlUer++RnRFkf9KCdWFSmokiim11fzzdiLANqIrU0NtrFsISzdAoEZmgEB7ZHoOVSnkw/DIwKk++LVeD7+jrRqwndqrcPCfF/6qVICS+E5Gslymmy3xtJT8yQVgNMDo7Rp9DS9nThjxLTYQzuMGDuGaMuuxxTXlL0FsELZMAQzK1+hQadZgsC2k74E0M1x8ihLxUlMO+mK1ALwk/w3eYIAMv7q1IHdZ4L3rfh99j/bU7cFvwMac0xMYzMn7vgYWv7yj/c6W5v/tJOgE13L7ntZy5b6RFnG9wQLn9kgoYBM9l5vc6G0zKBtFq358nhd7ZeHwJuwH6c6a1RwH2rcyboGKqMaFpEBPeidsjE8UwKMviqWTS8nXoLnmKqImLUOXwkx6piTGrLOMBMMPZw+V0MMer0KcUUyg2FZ4hhNipZuI1vCgTnuLzl1lU5DHBv6LvPiSm6L+fcFEe0QD2Ag7Yj0rUP0+EJtuDZngcbO1NrHX9Dg6rFBK/yY8rso= 9 | - secure: OWGZzBO1LQLOh3q1UXFXSR1woo5lm+5ZUJf5c6M5U9Wc/qOWTMFZKW81uKi64zZ84edLF9+cKfY0etJaJO9VhMtnYvU2jv/h2/5quqVQn1umZ3NlgqbB7GMg+j/aGje0FfOZlSiPH7HLXkfarRcH994+Xjm3A/HY1Sn/Y2J/SJt0lOqp0TaI5F2JrQ3Rjy8W5U3w9TyWn7UxatSYSW86Wi98gs2s7Qei6r1+vjUOhPLwpiQtfC3okjv1Hk9+i4jw1OsXiCfaKcNFpkQ9N87hPFBFFcPlebNMazQoAOgfnFzfo9Fy11NxBVzk44IxyOjQT6EMGk7f2XxtUBANzRoTrEqyG+6hP5qrCk7GBPIfol+xbPledqGjejybrsiXpmWCyuIF07LjcbuDZjMve0ip64vSHjKwVIs3liN2aA8SsPK0VvTAYv/EUt0IQTcsUQVxIpXnWyCmn450gDSK2t+zuzYlNuL+onnJ+kGn1dUyzocyTXLihnVCHIZirpRBAJizQsO2ciLmQCHqe85pgcWIkdcgToOpm925D/DECV4avyrsl/YPcoipuxzj6uGvQDCsWzo/PvAag5071FcdpqZno+r7qMSAiffmnL2+9twXyx8Pv0qLQNdfrRcIZUzXYP9VlkkKMIqQcNG+1kvrVnrKErSNLNSuHZDnBYkg7Z/wWhs= 10 | - secure: wfJv+rXTe5Z3xZwNGOoZO0pebha+6iq3qWhS3GuW9a46sojocQ6BVhbUfrRfrMo+avpABk0OT3sfbMRb7D+TQc9OtGtQjVp6cbNTnfv6pFdJnR6nWbhpGH5hQt08EjDxq8b53d5nCM+rfG0M5+2RAGi7el8P5QzpZ0T27dJ3hI2kFZLweSyu1UZMqskePtVLxmY7reIXHm73yWlQJkUUSFzogLqm/xQB8QNbuIMkTBtoQSVtWeTtDU7Lyg4ynvFgP6e9MMEn9M/i0KAN3R84ZUrdB9R6WF+NRoKukHEgNB7G1zPCMHGQKKwDZHlF5dcf9TSO4A0uegNT5FMPLAxTpe/d1XN5NW/nTMiswDD2E8rpcYsmTcVxj5Wc7aVaAu3P7FXOZEysvINwa5HR75s3qT1SyTMwSZL798lwfH+FZrtEMYZP7ZbKr8G2bZWB7jVCPFIZG/QVq00oYH9fPPMnGmVwCeK89JzEAJjXiwIcikPsGCfHnVMXboUPGT6EEmYhCxrrPEMK6lqtIojvuV+FZXzdmr9naujbmTOiQs2GvUFqhfsi3OWJ/YLnARcViyka90qd7neyDS0EmW9S5vTBww+DohwiXJEnOJRf9PTRS+hmj54l2xPDl1GMpwtRH6HdsX9pT0H6RVN06W+5J+JsgjqFhuPVQG+P5Z0U4UqqeRQ= 11 | - secure: tTUhMtY535udJMpryrx8z+9BbXbGOrLXA2GkJIbmsjrkJRGimubBls13tIKkPxdvBeRGSs0yy53ntVc145pTBid7GE9UrHjqrGU7vskXk1TC7optMsMD/yW3MriEM5JvqpXqiF0GLKTCvNZjYFXQr5H7Nliob71kYIflCQ6ojDO8M+U6NmX1XO+T67eEPEOBdKreKUtonEQIqQsXjRoxis5Csxt8XLyo4z5vVP6aW338tf05sYf5TqXUXoznyrL/IHoY5fhAsHB/LeEktsqfXQzCU4otJeP0cEJVr+TB0+S8HDMLLTGJPzgweOwXiB73Tjfn5sK7omuQIAVRqtEb+1hKmcAyD4/i8k9NSdxPwcwrB7Tq6Mzk8ZutIIS9wq6GwIV4L945tizIZUqLHOgHN5h/qUHdBWeazSHC+DRX03XXkr5hRyz3GQP404YlzO+MQzYfL+FuYNzbGW2HZJzgqjrghovevKqvp1iwg/f6/f33BSqd0NXxok/ww+G1myRGPaDDHhIsajKeDYZZGg7j5A9BGok4XP28d3adMHlQAksI0rOJRlUZ2GqKrK0frgH3U9g45iXNtn8coeKwnmlKiSgUSqfbTyyh0kR0WLFVFuDExcEF1Lz2l/pskjrIqZGseB71qdGgan0cMmxVC0vhdFz4YK7SvuyDzaa17J1666U= 12 | before_install: 13 | - openssl aes-256-cbc -K $encrypted_9e539fd55ef7_key -iv $encrypted_9e539fd55ef7_iv 14 | -in secring.gpg.enc -out secring.gpg -d 15 | deploy: 16 | provider: script 17 | script: release.sh 18 | on: 19 | tags: true 20 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/MutableHash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.function.BiFunction; 20 | import java.util.function.BiPredicate; 21 | 22 | /** 23 | * An interface extending {@link Hash} (which is immutable), with additional in-place methods to change the hash itself. 24 | * Those methods are generally named xxxInPlace 25 | */ 26 | public interface MutableHash extends Hash { 27 | @Override 28 | MutableHash put(K k, V v); 29 | 30 | @Override 31 | MutableHash putIfAbsent(K k, V v); 32 | 33 | @Override 34 | MutableHash filter(BiPredicate condition); 35 | 36 | @Override 37 | MutableHash reject(BiPredicate condition); 38 | 39 | @Override 40 | MutableHash invert(); 41 | 42 | @Override 43 | MutableHash merge(Hash another); 44 | 45 | @Override 46 | MutableHash remove(K k); 47 | 48 | @Override 49 | MutableHash remove(K k, V v); 50 | 51 | @Override 52 | Seq keysOf(V v); 53 | 54 | @Override 55 | MutableHash replace(K k, V v); 56 | 57 | @Override 58 | MutableHash replace(K k, V oldValue, V newValue); 59 | 60 | @Override 61 | MutableHash replaceAll(BiFunction function); 62 | 63 | 64 | /** 65 | * In-place method of {@link #put(K, V)} 66 | */ 67 | MutableHash putInPlace(K k, V v); 68 | 69 | /** 70 | * In-place method of {@link #putIfAbsent(K, V)} 71 | */ 72 | MutableHash putIfAbsentInPlace(K k, V v); 73 | 74 | /** 75 | * In-place method of {@link #remove(K)} 76 | */ 77 | MutableHash removeInPlace(K k); 78 | 79 | /** 80 | * In-place method of {@link #remove(K, V)} 81 | */ 82 | MutableHash removeInPlace(K k, V v); 83 | 84 | /** 85 | * In-place method of {@link #filter(BiPredicate)} 86 | */ 87 | MutableHash filterInPlace(BiPredicate condition); 88 | 89 | /** 90 | * In-place method of {@link #reject(BiPredicate)} 91 | */ 92 | MutableHash rejectInPlace(BiPredicate condition); 93 | 94 | /** 95 | * In-place method of {@link #merge(Hash)} 96 | */ 97 | MutableHash mergeInPlace(Hash another); 98 | 99 | /** 100 | * Remove all the key-value pair at this hash. 101 | * 102 | * @return the hash itself after clear 103 | */ 104 | MutableHash clear(); 105 | 106 | /** 107 | * In-place method of {@link #replace(K, V)} 108 | */ 109 | MutableHash replaceInPlace(K k, V v); 110 | /** 111 | * In-place method of {@link #replace(K, V, V)} 112 | */ 113 | MutableHash replaceInPlace(K k, V oldValue, V newValue); 114 | 115 | /** 116 | * In-place method of {@link #replaceAll(BiFunction)} 117 | */ 118 | MutableHash replaceAllInPlace(BiFunction function); 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IceFig [![Build Status](https://travis-ci.org/WorksApplications/icefig.svg?branch=master)](https://travis-ci.org/WorksApplications/icefig) [![Documentation](https://readthedocs.org/projects/svg-pottery/badge/?version=latest)](http://www.javadoc.io/doc/com.worksap/icefig) [![codecov.io](https://codecov.io/github/WorksApplications/icefig/coverage.svg?branch=master)](https://codecov.io/github/WorksApplications/icefig?branch=master) 2 | 3 | Java elegant supplement 4 | 5 | Java 8 delivered lambda expressions, but without the enhancement of basic libraries like List, Map, String, which makes 6 | lambda expression still not delightful. 7 | 8 | Inspired by other popular languages like Ruby and Scala, IceFig intends to supply the missing. 9 | 10 | ## Quick Scan 11 | 12 | Elegant alternative to List: **Seq** 13 | ```java 14 | Seq seq = Seqs.newSeq(1,2,3); 15 | seq.shuffle(); // copy to a new seq and shuffle it 16 | seq.forEach((value, idx) -> { // with index 17 | // (1, 0) (2, 1) (3, 2) 18 | }); 19 | seq.forEachCons(2, (values)->{ 20 | // [1,2] [2, 3] 21 | }); 22 | 23 | seq.join("-"); //"1-2-3" 24 | 25 | seq.map(a -> a+ 1).distinct().reverse().join() 26 | ``` 27 | 28 | Elegant alternative to Map: **Hash** 29 | ```java 30 | Hash hash = Hashes.newHash().put(1, 2).put(2, 3).put(3, 3); 31 | hash.containsAny((k, v) -> k+v == 5 ); //true 32 | hash.keysOf(3); // [2, 3] 33 | ``` 34 | 35 | Elegant alternative to String: **CharSeq** 36 | ```java 37 | CharSeq str = CharSeq.of("a b c d e f g"); 38 | str.split(" ").join("-").capitalize(); //"A-b-c-d-e-f-g" 39 | str.partition("d e").map(CharSeq::trim); //["a b c", "d e", "f g"] 40 | ``` 41 | 42 | [Full Javadoc](http://www.javadoc.io/doc/com.worksap/icefig) 43 | 44 | ## Include it 45 | 46 | ```xml 47 | 48 | com.worksap 49 | icefig 50 | [latest version] 51 | 52 | ``` 53 | 54 | 55 | ## Concept 56 | 57 | ### Not stream 58 | 59 | IceFig is different from Stream, and implemented without Stream. While, it is simpler concept -- supplement methods on basic libraries. 60 | 61 | Stream has several characteristics: 62 | 63 | 1. Trends to process each element independently 64 | 2. Infinite that we can not get the size 65 | 3. Designed for large data flow performance 66 | 67 | Thus, Stream may not be able to support operations related with the size of it, nor operations involving multiple or even random elements. 68 | 69 | Basically, the vast majority operations on List, Map, String don't need a Stream. Stream brings great merits on big data processing, but when we're not facing performance problem (operating a list of about 10x elements), it is an over kill. 70 | Yet Stream brings the 2 additional steps "Stream()" and "collect()", which is sometimes annoying to write. 71 | 72 | IceFig targets on "small data" operations within application logic, to provide simple & beautiful code writing about String, List, Map operation & transformation. 73 | 74 | ### No utilities 75 | 76 | In traditional Java way, we use a lot of utilities (StringUtils, FileUtils) for the missing methods in standard library. While in IceFig, we make an object oriented and functional way to free you from tedious codes. 77 | 78 | ### Zero runtime dependency 79 | 80 | IceFig has no external runtime dependency except JDK 8. 81 | 82 | ### Mutable & default interfaces 83 | 84 | IceFig firstly aggregates all operations which do not change the state into a default interface(e.x. Seq, Hash). 85 | On the other hand, there are interfaces named "mutableXXX" extending the default ones with additional in-place operations, which are commonly named `xxxInPlace`. 86 | 87 | If you don't want the ability to change the object, you can use the default interface to let compiler check it for you. And it is the recommended way. 88 | 89 | Note that the default interface doesn't mean immutability of the object it is on, it only ensures "if outside only uses this interface on the object, the object will not be changed". 90 | 91 | ### Conventions 92 | 93 | IceFig uses conventions on method names. If there is a pair of methods `name, nameInPlace`, method ends with `InPlace` means calling this method will change the object itself, while calling the other won't. 94 | 95 | 96 | ## License 97 | 98 | [Apache License 2.0](LICENSE) 99 | 100 | ## Contribution 101 | 102 | Feel free to submit issues & PRs 103 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/MutableSeq.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.*; 20 | import java.util.function.*; 21 | 22 | /** 23 | * An interface extending {@link Seq} (which is immutable), with additional in-place methods to change the seq itself. 24 | * Those methods are generally named xxxInPlace 25 | */ 26 | public interface MutableSeq extends Seq { 27 | 28 | @Override 29 | MutableSeq map(Function func); 30 | 31 | @Override 32 | MutableSeq map(BiFunction func); 33 | 34 | @Override 35 | MutableSeq flatMap(Function> func); 36 | 37 | @Override 38 | MutableSeq flatMap(BiFunction> func); 39 | 40 | @Override 41 | MutableSeq sample(int n); 42 | 43 | @Override 44 | MutableSeq shuffle(); 45 | 46 | @Override 47 | MutableSeq> eachCons(int n); 48 | 49 | @Override 50 | MutableSeq sort(Comparator comparator); 51 | 52 | @Override 53 | MutableSeq distinct(); 54 | 55 | @Override 56 | MutableSeq append(T value); 57 | 58 | @Override 59 | @SuppressWarnings({"varargs", "unchecked"}) 60 | MutableSeq append(T... values); 61 | 62 | @Override 63 | MutableSeq append(Collection collection); 64 | 65 | @Override 66 | MutableSeq append(Seq seq); 67 | 68 | @Override 69 | MutableSeq prepend(T value); 70 | 71 | @Override 72 | @SuppressWarnings({"varargs", "unchecked"}) 73 | MutableSeq prepend(T... values); 74 | 75 | @Override 76 | MutableSeq prepend(Collection collection); 77 | 78 | @Override 79 | MutableSeq prepend(Seq seq); 80 | 81 | @Override 82 | MutableSeq subSeq(int fromIndex, int toIndex); 83 | 84 | @Override 85 | MutableSeq reject(Predicate condition); 86 | 87 | @Override 88 | MutableSeq reject(BiPredicate condition); 89 | 90 | @Override 91 | MutableSeq rejectWhile(Predicate condition); 92 | 93 | @Override 94 | MutableSeq rejectWhile(BiPredicate condition); 95 | 96 | @Override 97 | MutableSeq filter(Predicate condition); 98 | 99 | @Override 100 | MutableSeq filter(BiPredicate condition); 101 | 102 | @Override 103 | MutableSeq filterWhile(Predicate condition); 104 | 105 | @Override 106 | MutableSeq filterWhile(BiPredicate condition); 107 | 108 | @Override 109 | MutableSeq repeat(int times); 110 | 111 | @Override 112 | default MutableSeq compact() { 113 | return this.reject(e -> e == null); 114 | } 115 | 116 | @Override 117 | MutableSeq> eachSlice(int n); 118 | 119 | @Override 120 | MutableSeq reverse(); 121 | 122 | @Override 123 | MutableSeq> eachCombination(int n); 124 | 125 | /** 126 | * In-place method of {@link #append(Object)} 127 | */ 128 | MutableSeq appendInPlace(T value); 129 | 130 | /** 131 | * In-place method of {@link #append(T...)} 132 | */ 133 | @SuppressWarnings({"varargs", "unchecked"}) 134 | MutableSeq appendInPlace(T... values); 135 | 136 | /** 137 | * In-place method of {@link #append(Collection)} 138 | */ 139 | MutableSeq appendInPlace(Collection collection); 140 | 141 | /** 142 | * In-place method of {@link #append(Seq)} 143 | */ 144 | MutableSeq appendInPlace(Seq seq); 145 | 146 | /** 147 | * In-place method of {@link #prepend(Object)} 148 | */ 149 | MutableSeq prependInPlace(T value); 150 | 151 | /** 152 | * In-place method of {@link #prepend(T...)} 153 | */ 154 | @SuppressWarnings({"varargs", "unchecked"}) 155 | MutableSeq prependInPlace(T... values); 156 | 157 | /** 158 | * In-place method of {@link #prepend(Collection)} 159 | */ 160 | MutableSeq prependInPlace(Collection collection); 161 | 162 | /** 163 | * In-place method of {@link #prepend(Seq)} 164 | */ 165 | MutableSeq prependInPlace(Seq seq); 166 | 167 | 168 | /** 169 | * Remove all elements in the seq. 170 | * 171 | * @return The seq itself after changed. 172 | */ 173 | MutableSeq clear(); 174 | 175 | /** 176 | * Update the element at the index. 177 | * 178 | * @return The seq itself after changed. 179 | */ 180 | MutableSeq set(int i, T t); 181 | 182 | /** 183 | * In-place method of {@link #shuffle()} 184 | */ 185 | MutableSeq shuffleInPlace(); 186 | 187 | /** 188 | * In-place method of {@link #reverse()} 189 | */ 190 | MutableSeq reverseInPlace(); 191 | 192 | /** 193 | * In-place method of {@link #distinct()} 194 | */ 195 | MutableSeq distinctInPlace(); 196 | 197 | /** 198 | * In-place method of {@link #repeat(int)} 199 | */ 200 | MutableSeq repeatInPlace(int times); 201 | 202 | /** 203 | * In-place method of {@link #compact()} 204 | */ 205 | MutableSeq compactInPlace(); 206 | 207 | /** 208 | * In-place method of {@link #sort(Comparator)} 209 | */ 210 | MutableSeq sortInPlace(Comparator comparator); 211 | 212 | 213 | /** 214 | * In-place method of {@link #filter(Predicate)} 215 | */ 216 | MutableSeq filterInPlace(Predicate condition); 217 | 218 | /** 219 | * In-place method of {@link #filter(BiPredicate)} 220 | */ 221 | MutableSeq filterInPlace(BiPredicate condition); 222 | 223 | /** 224 | * In-place method of {@link #filterWhile(Predicate)} 225 | */ 226 | MutableSeq filterWhileInPlace(Predicate condition); 227 | 228 | /** 229 | * In-place method of {@link #filterWhile(BiPredicate)} 230 | */ 231 | MutableSeq filterWhileInPlace(BiPredicate condition); 232 | 233 | /** 234 | * In-place method of {@link #reject(Predicate)} 235 | */ 236 | MutableSeq rejectInPlace(Predicate condition); 237 | 238 | /** 239 | * In-place method of {@link #reject(BiPredicate)} 240 | */ 241 | MutableSeq rejectInPlace(BiPredicate condition); 242 | 243 | /** 244 | * In-place method of {@link #rejectWhile(Predicate)} 245 | */ 246 | MutableSeq rejectWhileInPlace(Predicate condition); 247 | 248 | /** 249 | * In-place method of {@link #rejectWhile(BiPredicate)} 250 | */ 251 | MutableSeq rejectWhileInPlace(BiPredicate condition); 252 | 253 | /** 254 | * In-place method of {@link #swap(int, int)} 255 | */ 256 | MutableSeq swapInPlace(int i, int j); 257 | 258 | /** 259 | * In-place method of {@link #rotate(int)} 260 | */ 261 | MutableSeq rotateInPlace(int distance); 262 | } 263 | -------------------------------------------------------------------------------- /src/test/java/com/worksap/icefig/lang/RangeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | 20 | import org.junit.Test; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.Collections; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | import java.util.NoSuchElementException; 28 | import java.util.function.BiFunction; 29 | import java.util.function.Function; 30 | 31 | import static org.hamcrest.CoreMatchers.equalTo; 32 | import static org.hamcrest.CoreMatchers.is; 33 | import static org.junit.Assert.assertThat; 34 | 35 | public class RangeTest { 36 | @Test 37 | public void testConstruct() { 38 | Range range = new Range<>(1); 39 | assertThat(range.getFrom(), is(1)); 40 | 41 | range = new Range<>(2, 10, i -> i + 2); 42 | assertThat(range.getFrom(), is(2)); 43 | assertThat(range.getTo(), is(10)); 44 | assertThat(range.isToIncluded(), is(true)); 45 | } 46 | 47 | @Test 48 | public void testFrom() { 49 | assertThat(new Range<>(1).from(2).getFrom(), is(2)); 50 | Helpers.assertThrows(NullPointerException.class, () -> new Range<>("").from(null)); 51 | } 52 | 53 | @Test 54 | public void testTo() { 55 | Range range = new Range<>('a').to('z'); 56 | assertThat(range.getTo(), is('z')); 57 | assertThat(range.isToIncluded(), is(true)); 58 | Helpers.assertThrows(NullPointerException.class, () -> new Range<>("").to(null)); 59 | } 60 | 61 | @Test 62 | public void testUntil() { 63 | Range range = new Range<>('a').until('z'); 64 | assertThat(range.getTo(), is('z')); 65 | assertThat(range.isToIncluded(), is(false)); 66 | Helpers.assertThrows(NullPointerException.class, () -> new Range<>("").until(null)); 67 | } 68 | 69 | @Test 70 | public void testNext() { 71 | Function next = null; 72 | Helpers.assertThrows(NullPointerException.class, () -> new Range<>("").next(next)); 73 | 74 | BiFunction biNext = null; 75 | Helpers.assertThrows(NullPointerException.class, () -> new Range<>("").next(biNext)); 76 | } 77 | 78 | @Test 79 | public void testForEach() { 80 | List list = new ArrayList<>(); 81 | new Range<>(1).to(64).next(i -> i + i).forEach(e -> list.add(e)); 82 | assertThat(list, equalTo(Arrays.asList(1, 2, 4, 8, 16, 32, 64))); 83 | 84 | list.clear(); 85 | new Range<>(1).until(64).next(i -> i + i).forEach(e -> list.add(e)); 86 | assertThat(list, equalTo(Arrays.asList(1, 2, 4, 8, 16, 32))); 87 | 88 | list.clear(); 89 | new Range<>(64).to(1).next(i -> i / 2).forEach(e -> list.add(e)); 90 | assertThat(list, equalTo(Arrays.asList(64, 32, 16, 8, 4, 2, 1))); 91 | 92 | list.clear(); 93 | new Range<>(1).to(1).next(i -> i + i).forEach(e -> list.add(e)); 94 | assertThat(list, equalTo(Arrays.asList(1))); 95 | 96 | list.clear(); 97 | new Range<>(1).until(1).next(i -> i + i).forEach(e -> list.add(e)); 98 | assertThat(list, equalTo(Collections.emptyList())); 99 | 100 | list.clear(); 101 | List indices = new ArrayList<>(); 102 | new Range<>(1).until(64).next(i -> i + i) 103 | .forEach((e, i) -> { 104 | list.add(e); 105 | indices.add(i); 106 | }); 107 | assertThat(list, equalTo(Arrays.asList(1, 2, 4, 8, 16, 32))); 108 | assertThat(indices, equalTo(Arrays.asList(0, 1, 2, 3, 4, 5))); 109 | 110 | list.clear(); 111 | new Range<>(1).to(720).next((c, i) -> c * (i + 2)).forEach(e -> list.add(e)); 112 | assertThat(list, equalTo(Arrays.asList(1, 2, 6, 24, 120, 720))); 113 | 114 | list.clear(); 115 | new Range<>(1).to(5).next(i -> i + 1).next((c, i) -> c * (i + 2)).forEach(e -> list.add(e)); 116 | assertThat(list, equalTo(Arrays.asList(1, 2, 3, 4, 5))); 117 | 118 | list.clear(); 119 | Helpers.assertThrows(NullPointerException.class, () -> new Range<>(1).to(10).forEach(e -> list.add(e))); 120 | } 121 | 122 | @Test 123 | public void testToSeq() { 124 | assertThat(new Range<>(1).to(1).next(i -> i + 1).toSeq(), is(equalTo(Seqs.newSeq(1)))); 125 | assertThat(new Range<>(1).until(1).next(i -> i + 1).toSeq(), is(equalTo(Seqs.newSeq()))); 126 | assertThat(new Range<>('a').to('e').next(c -> (char) (c + 1)).toSeq(), is(equalTo(Seqs.newSeq('a', 'b', 'c', 'd', 'e')))); 127 | assertThat(new Range<>(6).until(0).next(i -> i - 2).toSeq(), is(equalTo(Seqs.newSeq(6, 4, 2)))); 128 | } 129 | 130 | @Test 131 | public void testToMutableSeq() { 132 | assertThat(new Range<>(1).to(5).next(i -> i + 1).toMutableSeq(), is(equalTo(Seqs.newMutableSeq(1, 2, 3, 4, 5)))); 133 | } 134 | 135 | @Test 136 | public void testIterator() { 137 | Iterator itr = new Range<>(1).to(1).next(i -> i + 1).iterator(); 138 | assertThat(itr.next(), is(1)); 139 | assertThat(itr.hasNext(), is(false)); 140 | final Iterator it = itr; 141 | Helpers.assertThrows(NoSuchElementException.class, () -> it.next()); 142 | 143 | itr = new Range<>(1).to(2).next(i -> i + 1).iterator(); 144 | assertThat(itr.next(), is(1)); 145 | assertThat(itr.next(), is(2)); 146 | assertThat(itr.hasNext(), is(false)); 147 | 148 | itr = new Range<>(1).next(i -> i + 1).iterator(); 149 | assertThat(itr.hasNext(), is(true)); 150 | for (int i = 0; i < 100; i++) { 151 | itr.next(); 152 | } 153 | assertThat(itr.next(), is(101)); 154 | assertThat(itr.hasNext(), is(true)); 155 | 156 | List list = new ArrayList<>(); 157 | for (int d: new Range<>(1).next(i -> i + 1).until(5)) { 158 | list.add(d); 159 | } 160 | assertThat(list, equalTo(Arrays.asList(1, 2, 3, 4))); 161 | } 162 | 163 | @Test 164 | public void testSpliterator() { 165 | Helpers.assertThrows(IllegalArgumentException.class, () -> new Range<>(1).next(i -> i + 1).take(-1)); 166 | } 167 | 168 | @Test 169 | public void testTake() { 170 | assertThat(new Range<>(1).next(i -> i + 1).take(0), equalTo(Seqs.newMutableSeq())); 171 | assertThat(new Range<>(1).next(i -> i + 1).take(5), equalTo(Seqs.newMutableSeq(1, 2, 3, 4, 5))); 172 | assertThat(new Range<>(1).next(i -> i + 1).to(3).take(5), equalTo(Seqs.newMutableSeq(1, 2, 3))); 173 | 174 | Helpers.assertThrows(UnsupportedOperationException.class, () -> new Range<>(1).spliterator()); 175 | } 176 | 177 | @Test 178 | public void testTakeWhile() { 179 | assertThat(new Range<>(1).next(i -> i + 1).takeWhile(e -> e <= 5), equalTo(Seqs.newMutableSeq(1, 2, 3, 4, 5))); 180 | assertThat(new Range<>(1).next(i -> i + 1).takeWhile(e -> e < 1), equalTo(Seqs.newMutableSeq())); 181 | assertThat(new Range<>(1).next(i -> i + 1).until(1).takeWhile(e -> e <= 5), equalTo(Seqs.newMutableSeq())); 182 | assertThat(new Range<>(1).next(i -> i + 1).takeWhile((e, i) -> i < 5), equalTo(Seqs.newMutableSeq(1, 2, 3, 4, 5))); 183 | assertThat(new Range<>(1).next(i -> i + 1).takeWhile((e, i) -> i < 0), equalTo(Seqs.newMutableSeq())); 184 | assertThat(new Range<>(1).next(i -> i + 1).until(1).takeWhile((e, i) -> i < 5), equalTo(Seqs.newMutableSeq())); 185 | 186 | List indices = new ArrayList<>(); 187 | assertThat(new Range<>(1).next(i -> i + i) 188 | .takeWhile((e, i) -> { 189 | indices.add(i); 190 | return e < 64; 191 | }), equalTo(Seqs.newMutableSeq(1, 2, 4, 8, 16, 32))); 192 | assertThat(indices, equalTo(Arrays.asList(0, 1, 2, 3, 4, 5, 6))); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/HashImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.HashMap; 20 | import java.util.Iterator; 21 | import java.util.Map; 22 | import java.util.HashMap; 23 | import java.util.Objects; 24 | import java.util.function.BiFunction; 25 | import java.util.function.BiPredicate; 26 | 27 | /** 28 | * Created by lijunxiao on 8/6/15. 29 | */ 30 | class HashImpl implements MutableHash { 31 | private HashMap hash; 32 | 33 | protected HashImpl() { 34 | this.hash = new HashMap<>(); 35 | } 36 | 37 | protected HashImpl(Map m) { 38 | this.hash = new HashMap<>(m); 39 | } 40 | 41 | @Override 42 | public boolean containsAny(BiPredicate condition) { 43 | for (Map.Entry entry : hash.entrySet()) { 44 | if (condition.test(entry.getKey(), entry.getValue())) { 45 | return true; 46 | } 47 | } 48 | return false; 49 | } 50 | 51 | @Override 52 | public boolean containsKey(K k) { 53 | return hash.containsKey(k); 54 | } 55 | 56 | @Override 57 | public boolean containsValue(V v) { 58 | return hash.containsValue(v); 59 | } 60 | 61 | @Override 62 | public boolean isEmpty() { 63 | return hash.isEmpty(); 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (o == this) 69 | return true; 70 | if (o instanceof HashImpl) { 71 | HashImpl h = (HashImpl)o; 72 | return hash.equals(h.hash); 73 | } 74 | return false; 75 | } 76 | 77 | @Override 78 | public int size() { 79 | return hash.size(); 80 | } 81 | 82 | @Override 83 | public V get(K k) { 84 | return hash.get(k); 85 | } 86 | 87 | @Override 88 | public Seq values() { 89 | return Seqs.newMutableSeq(hash.values()); 90 | } 91 | 92 | @Override 93 | public Seq keys() { 94 | return Seqs.newMutableSeq(hash.keySet()); 95 | } 96 | 97 | @Override 98 | public Seq> entrySeq() { 99 | return Seqs.newMutableSeq(hash.entrySet()); 100 | } 101 | 102 | @Override 103 | public MutableHash put(K k, V v) { 104 | Map newHash = new HashMap<>(hash); 105 | newHash.put(k, v); 106 | return new HashImpl<>(newHash); 107 | } 108 | 109 | @Override 110 | public MutableHash putIfAbsent(K k, V v) { 111 | Map newHash = new HashMap<>(hash); 112 | newHash.putIfAbsent(k, v); 113 | return new HashImpl<>(newHash); 114 | } 115 | 116 | @Override 117 | public MutableHash filter(BiPredicate condition) { 118 | Objects.requireNonNull(condition); 119 | Map newHash = new HashMap<>(); 120 | 121 | this.hash.forEach((k, v) -> { 122 | if (condition.test(k, v)) { 123 | newHash.put(k, v); 124 | } 125 | }); 126 | return new HashImpl<>(newHash); 127 | } 128 | 129 | @Override 130 | public MutableHash reject(BiPredicate condition) { 131 | Objects.requireNonNull(condition); 132 | Map newHash = new HashMap<>(); 133 | 134 | hash.forEach((k, v) -> { 135 | if (!condition.test(k, v)) { 136 | newHash.put(k, v); 137 | } 138 | }); 139 | return new HashImpl<>(newHash); 140 | } 141 | 142 | @Override 143 | public MutableHash invert() { 144 | Map newHash = new HashMap<>(); 145 | 146 | hash.forEach((k, v) -> newHash.put(v, k)); 147 | return new HashImpl<>(newHash); 148 | } 149 | 150 | @Override 151 | public MutableHash merge(Hash another) { 152 | Map newHash = new HashMap<>(hash); 153 | if (another != null) { 154 | another.entrySeq().forEach(entry -> { 155 | newHash.put(entry.getKey(), entry.getValue()); 156 | }); 157 | } 158 | return new HashImpl<>(newHash); 159 | } 160 | 161 | @Override 162 | public MutableHash remove(K k) { 163 | Map newHash = new HashMap<>(hash); 164 | newHash.remove(k); 165 | return new HashImpl<>(newHash); 166 | } 167 | 168 | @Override 169 | public MutableHash remove(K k, V v) { 170 | Map newHash = new HashMap<>(hash); 171 | newHash.remove(k, v); 172 | return new HashImpl<>(newHash); 173 | } 174 | 175 | @Override 176 | public MutableHash filterInPlace(BiPredicate condition) { 177 | Objects.requireNonNull(condition); 178 | final Iterator> each = hash.entrySet().iterator(); 179 | while (each.hasNext()) { 180 | Map.Entry nextEntry = each.next(); 181 | if (!condition.test(nextEntry.getKey(), nextEntry.getValue())) { 182 | each.remove(); 183 | } 184 | } 185 | return this; 186 | } 187 | 188 | @Override 189 | public MutableHash rejectInPlace(BiPredicate condition) { 190 | Objects.requireNonNull(condition); 191 | final Iterator> each = hash.entrySet().iterator(); 192 | while (each.hasNext()) { 193 | Map.Entry nextEntry = each.next(); 194 | if (condition.test(nextEntry.getKey(), nextEntry.getValue())) { 195 | each.remove(); 196 | } 197 | } 198 | return this; 199 | } 200 | 201 | @Override 202 | public MutableHash mergeInPlace(Hash another) { 203 | if (another != null) { 204 | another.entrySeq().forEach(entry -> { 205 | hash.put(entry.getKey(), entry.getValue()); 206 | }); 207 | } 208 | return this; 209 | } 210 | 211 | @Override 212 | public MutableHash clear() { 213 | hash.clear(); 214 | return this; 215 | } 216 | 217 | @Override 218 | public MutableHash replaceInPlace(K k, V v) { 219 | hash.replace(k, v); 220 | return this; 221 | } 222 | 223 | @Override 224 | public MutableHash replaceInPlace(K k, V oldValue, V newValue) { 225 | hash.replace(k, oldValue, newValue); 226 | return this; 227 | } 228 | 229 | @Override 230 | public MutableHash replaceAllInPlace(BiFunction function) { 231 | Objects.requireNonNull(function); 232 | hash.replaceAll(function); 233 | return this; 234 | } 235 | 236 | @Override 237 | public MutableHash putInPlace(K k, V v) { 238 | hash.put(k, v); 239 | return this; 240 | } 241 | 242 | @Override 243 | public MutableHash putIfAbsentInPlace(K k, V v) { 244 | hash.putIfAbsent(k, v); 245 | return this; 246 | } 247 | 248 | @Override 249 | public MutableHash removeInPlace(K k) { 250 | hash.remove(k); 251 | return this; 252 | } 253 | 254 | @Override 255 | public MutableHash removeInPlace(K k, V v) { 256 | hash.remove(k, v); 257 | return this; 258 | } 259 | 260 | @Override 261 | public Seq keysOf(V value) { 262 | MutableSeq result = Seqs.newMutableSeq(); 263 | hash.forEach((k, v) -> { 264 | if (value == null && v == null) 265 | result.appendInPlace(k); 266 | else if (value != null && v != null && v.equals(value)) 267 | result.appendInPlace(k); 268 | }); 269 | return result; 270 | } 271 | 272 | @Override 273 | public MutableHash replace(K k, V v) { 274 | Map newHash = new HashMap<>(hash); 275 | newHash.replace(k, v); 276 | return new HashImpl<>(newHash); 277 | } 278 | 279 | @Override 280 | public MutableHash replace(K k, V oldValue, V newValue) { 281 | Map newHash = new HashMap<>(hash); 282 | newHash.replace(k, oldValue, newValue); 283 | return new HashImpl<>(newHash); 284 | } 285 | 286 | @Override 287 | public MutableHash replaceAll(BiFunction function) { 288 | Objects.requireNonNull(function); 289 | Map newHash = new HashMap<>(hash); 290 | newHash.replaceAll(function); 291 | return new HashImpl<>(newHash); 292 | } 293 | 294 | @Override 295 | public int count(V value) { 296 | return this.values().count(value); 297 | } 298 | 299 | @Override 300 | public int countIf(BiPredicate condition) { 301 | Objects.requireNonNull(condition); 302 | int count = 0; 303 | final Iterator> each = hash.entrySet().iterator(); 304 | while (each.hasNext()) { 305 | Map.Entry nextEntry = each.next(); 306 | if (condition.test(nextEntry.getKey(), nextEntry.getValue())) { 307 | count++; 308 | } 309 | } 310 | return count; 311 | } 312 | 313 | @Override 314 | public HashMap toHashMap(){ 315 | return this.hash; 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/Range.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Objects; 22 | import java.util.Optional; 23 | import java.util.Spliterator; 24 | import java.util.function.BiConsumer; 25 | import java.util.function.BiFunction; 26 | import java.util.function.BiPredicate; 27 | import java.util.function.Consumer; 28 | import java.util.function.Function; 29 | import java.util.function.Predicate; 30 | 31 | /** 32 | * Range is an element generator on the basis of start point, 33 | * end point and next element function. 34 | */ 35 | public class Range> implements Iterable { 36 | private C from; 37 | private C to; 38 | private boolean toIncluded; 39 | private Function next; 40 | private BiFunction biNext; 41 | 42 | /** 43 | * @param from The start point. 44 | * @throws NullPointerException if from is null. 45 | */ 46 | public Range(C from) { 47 | this.from(from); 48 | } 49 | 50 | /** 51 | * @param from The start point. 52 | * @param to The end point. It is included in the range. 53 | * @param next The next element generator. 54 | * @throws NullPointerException if from, to or next is null. 55 | */ 56 | public Range(C from, C to, Function next) { 57 | this.from(from); 58 | this.to(to); 59 | this.next(next); 60 | } 61 | 62 | /** 63 | * Set start point 64 | * 65 | * @param from The start point. 66 | * @return Current range object. 67 | * @throws NullPointerException if from is null. 68 | */ 69 | public Range from(C from) { 70 | Objects.requireNonNull(from); 71 | this.from = from; 72 | return this; 73 | } 74 | 75 | /** 76 | * Set end point. The end point is included in this range. 77 | * 78 | * @param to The end point. 79 | * @return Current range object. 80 | * @throws NullPointerException if to is null. 81 | */ 82 | public Range to(C to) { 83 | Objects.requireNonNull(to); 84 | this.to = to; 85 | toIncluded = true; 86 | return this; 87 | } 88 | 89 | /** 90 | * Set end point. The end point is excluded in this range. 91 | * 92 | * @param to The end point. 93 | * @return Current range object. 94 | * @throws NullPointerException if to is null. 95 | */ 96 | public Range until(C to) { 97 | Objects.requireNonNull(to); 98 | this.to = to; 99 | toIncluded = false; 100 | return this; 101 | } 102 | 103 | /** 104 | * Set next element generator. 105 | * 106 | * @param next The next element generator 107 | * @return Current range object. 108 | * @throws NullPointerException if next is null. 109 | */ 110 | public Range next(Function next) { 111 | Objects.requireNonNull(next); 112 | this.next = next; 113 | return this; 114 | } 115 | 116 | /** 117 | * Set next element generator. 118 | * 119 | * @param next The next element generator 120 | * @return Current range object. 121 | * @throws NullPointerException if next is null. 122 | */ 123 | public Range next(BiFunction next) { 124 | Objects.requireNonNull(next); 125 | this.biNext = next; 126 | return this; 127 | } 128 | 129 | /** 130 | * @return The start point 131 | */ 132 | public C getFrom() { 133 | return from; 134 | } 135 | 136 | /** 137 | * @return The end point 138 | */ 139 | public C getTo() { 140 | return to; 141 | } 142 | 143 | /** 144 | * @return Whether the end point is included in this range. 145 | */ 146 | public boolean isToIncluded() { 147 | return toIncluded; 148 | } 149 | 150 | /** 151 | * Iterate each element of the range. 152 | * 153 | * @throws NullPointerException if action, this.from, this.to or this.next is null. 154 | */ 155 | public void forEach(Consumer action) { 156 | forEach((e, i) -> action.accept(e)); 157 | } 158 | 159 | /** 160 | * Similar to {@link #forEach(Consumer)}, with additional parameter "index" as the second parameter of the lambda expression. 161 | * 162 | * @throws NullPointerException if action, this.from, this.to or this.next is null. 163 | */ 164 | public void forEach(BiConsumer action) { 165 | Objects.requireNonNull(action); 166 | Objects.requireNonNull(to); 167 | 168 | Itr itr = new Itr(); 169 | while (itr.hasNext()) { 170 | int idx = itr.cursor; 171 | C current = itr.next(); 172 | action.accept(current, idx); 173 | } 174 | } 175 | 176 | public Seq toSeq() { 177 | MutableSeq seq = Seqs.newMutableSeq(); 178 | forEach((Consumer) seq::appendInPlace); 179 | return seq; 180 | } 181 | 182 | public MutableSeq toMutableSeq() { 183 | MutableSeq seq = Seqs.newMutableSeq(); 184 | forEach((Consumer) seq::appendInPlace); 185 | return seq; 186 | } 187 | 188 | /** 189 | * 190 | * @throws NullPointerException if this.from or this.next is null. 191 | */ 192 | @Override 193 | public Iterator iterator() { 194 | return new Itr(); 195 | } 196 | 197 | @Override 198 | public Spliterator spliterator() { 199 | throw new UnsupportedOperationException("spliterator"); 200 | } 201 | 202 | /** 203 | * Get the first n elements of this range. 204 | * 205 | * @return The Seq containing the first n elements. 206 | * @throws IllegalArgumentException if n < 0 207 | */ 208 | public Seq take(int n) { 209 | if (n < 0) { 210 | throw new IllegalArgumentException("n"); 211 | } 212 | Itr itr = new Itr(); 213 | MutableSeq seq = Seqs.newMutableSeq(); 214 | 215 | while (itr.hasNext() && itr.cursor < n) { 216 | seq.appendInPlace(itr.next()); 217 | } 218 | 219 | return seq; 220 | } 221 | 222 | /** 223 | * Get elements at the front of this range which satisfy the condition. 224 | * 225 | * @param condition the condition used to filter elements by passing the element, 226 | * returns true if the element satisfies the condition, otherwise returns false. 227 | * @return The seq containing all the elements satisfying the condition 228 | * @throws NullPointerException if condition is null 229 | */ 230 | public Seq takeWhile(Predicate condition) { 231 | Objects.requireNonNull(condition); 232 | 233 | Itr itr = new Itr(); 234 | MutableSeq seq = Seqs.newMutableSeq(); 235 | 236 | while (itr.hasNext()) { 237 | C candidate = itr.next(); 238 | if (!condition.test(candidate)) { 239 | break; 240 | } 241 | seq.appendInPlace(candidate); 242 | } 243 | 244 | return seq; 245 | } 246 | 247 | /** 248 | * Get elements at the front of this range which satisfy the condition. 249 | *

250 | * Similar to {@link #takeWhile(Predicate)}, with additional parameter "index" as the second parameter of the lambda expression. 251 | *

252 | * 253 | * @param condition the condition used to filter elements by passing the element and its index, 254 | * returns true if the element satisfies the condition, otherwise returns false. 255 | * @return The seq containing all the elements satisfying the condition 256 | * @throws NullPointerException if condition is null 257 | */ 258 | public Seq takeWhile(BiPredicate condition) { 259 | Objects.requireNonNull(condition); 260 | 261 | Itr itr = new Itr(); 262 | MutableSeq seq = Seqs.newMutableSeq(); 263 | 264 | while (itr.hasNext()) { 265 | int idx = itr.cursor; 266 | C candidate = itr.next(); 267 | if (!condition.test(candidate, idx)) { 268 | break; 269 | } 270 | seq.appendInPlace(candidate); 271 | } 272 | 273 | return seq; 274 | } 275 | 276 | private class Itr implements Iterator { 277 | int cursor; 278 | C current; 279 | C last; 280 | C end; 281 | boolean endIncluded; 282 | 283 | /* 284 | * orientation = 0 means to is equal to from; 285 | * orientation > 0 means to is greater than from; 286 | * otherwise to is less than from. 287 | */ 288 | final Optional orientation; 289 | 290 | private Itr() { 291 | Objects.requireNonNull(from); 292 | 293 | if (Objects.isNull(biNext)) { 294 | Objects.requireNonNull(next); 295 | } 296 | 297 | current = from; 298 | end = to; 299 | endIncluded = toIncluded; 300 | 301 | if (Objects.isNull(end)) { 302 | orientation = Optional.empty(); 303 | } else { 304 | orientation = Optional.of(current.compareTo(end)); 305 | } 306 | } 307 | 308 | @Override 309 | public boolean hasNext() { 310 | if (!orientation.isPresent()) { 311 | return true; 312 | } 313 | int cmp = current.compareTo(end); 314 | return cmp * orientation.get() > 0 315 | || endIncluded && cmp == 0; 316 | } 317 | 318 | @Override 319 | public C next() { 320 | if (!hasNext()) { 321 | throw new NoSuchElementException(); 322 | } 323 | 324 | last = current; 325 | if (Objects.nonNull(next)) { 326 | current = next.apply(current); 327 | } else { 328 | current = biNext.apply(current, cursor); 329 | } 330 | Objects.requireNonNull(current); 331 | ++ cursor; 332 | 333 | return last; 334 | } 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/Hash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.ConcurrentModificationException; 20 | import java.util.Map; 21 | import java.util.HashMap; 22 | import java.util.function.BiFunction; 23 | import java.util.function.BiPredicate; 24 | 25 | /** 26 | * Elegant supplement for Map in JDK 27 | */ 28 | public interface Hash { 29 | /** 30 | * Check whether this hash contains any key-value pair that satisfies the condition. 31 | * 32 | * @param condition the condition used to filter key-value pairs by passing the key and value of the pair, 33 | * returns true if the key-value pair satisfies the condition, otherwise returns false. 34 | * @return whether this hash contains any key-value pair that satisfies the condition 35 | * @throws NullPointerException if condition is null 36 | */ 37 | boolean containsAny(BiPredicate condition); 38 | 39 | /** 40 | * Returns true if this hash contains a mapping for the specified 41 | * key. More formally, returns true if and only if 42 | * this hash contains a mapping for a key k such that 43 | * (key==null ? k==null : key.equals(k)). (There can be 44 | * at most one such mapping.) 45 | * 46 | * @param key key whose presence in this hash is to be tested 47 | * @return true if this hash contains a mapping for the specified 48 | * key 49 | * @throws ClassCastException if the key is of an inappropriate type for 50 | * this map 51 | * @throws NullPointerException if the specified key is null and this map 52 | * does not permit null keys 53 | */ 54 | boolean containsKey(K key); 55 | 56 | boolean containsValue(V value); 57 | 58 | boolean isEmpty(); 59 | 60 | int size(); 61 | 62 | V get(K k); 63 | 64 | /** 65 | * Returns a {@link Seq} view of the values contained in this hash. 66 | * 67 | * @return a seq view of the values contained in this hash 68 | */ 69 | Seq values(); 70 | 71 | /** 72 | * Returns a {@link Seq} view of the keys contained in this hash. 73 | * 74 | * @return a seq view of the keys contained in this hash 75 | */ 76 | Seq keys(); 77 | 78 | /** 79 | * Returns a {@link Seq} view of the mappings contained in this hash. 80 | * 81 | * @return a seq view of the mappings contained in this hash 82 | */ 83 | Seq> entrySeq(); 84 | 85 | Hash put(K k, V v); 86 | 87 | Hash putIfAbsent(K k, V v); 88 | 89 | /** 90 | * Return a new hash with the key-value pairs of the original Hash which satisfy the condition. 91 | * 92 | * @param condition the condition used to filter key-value pairs by passing the key and value of the pair, 93 | * returns true if the key-value pair satisfies the condition, otherwise returns false. 94 | * @return a new with only key-value pairs which satisfy the condition 95 | * @throws NullPointerException if condition is null 96 | */ 97 | Hash filter(BiPredicate condition); 98 | 99 | /** 100 | * Return a new hash with the key-value pairs of the original Hash which don't satisfy the condition. 101 | * 102 | * @param condition the condition used to filter key-value pairs by passing the key and value of the pair, 103 | * returns true if the key-value pair satisfies the condition, otherwise returns false. 104 | * @return a new hash with key-value pairs which don't satisfy the condition 105 | * @throws NullPointerException if condition is null 106 | */ 107 | Hash reject(BiPredicate condition); 108 | 109 | /** 110 | * Returns a new hash created by using hash’s values as keys, and the keys as values. 111 | * If there are duplicated values, the last key is kept. 112 | * Since it is hash map, the order of keys is decided by hash table. 113 | * 114 | * @return an inverted hash 115 | */ 116 | Hash invert(); 117 | 118 | /** 119 | * Returns a new hash containing the mappings of the specified hash and this hash itself. 120 | * The value for entries with duplicate keys will be that of the specified hash. 121 | * 122 | * @param another the specified hash to be merged 123 | * @return the new hash containing all the mappings of the specified hash and this hash itself 124 | */ 125 | Hash merge(Hash another); 126 | 127 | /** 128 | * Removes the mapping for a key from this map if it is present 129 | * (optional operation). More formally, if this hash contains a mapping 130 | * from key k to value v such that 131 | * (key==null ? k==null : key.equals(k)), that mapping 132 | * is removed. (The map can contain at most one such mapping.) 133 | * 134 | * @param key key whose mapping is to be removed from the map 135 | * @return a new hash after the key is removed 136 | * @throws UnsupportedOperationException if the remove operation 137 | * is not supported by this hash 138 | * @throws ClassCastException if the key is of an inappropriate type for 139 | * this hash 140 | * @throws NullPointerException if the specified key is null and this 141 | * hash does not permit null keys 142 | */ 143 | Hash remove(K key); 144 | 145 | /** 146 | * Removes the entry for the specified key only if it is currently 147 | * mapped to the specified value. 148 | * 149 | * @param key key with which the specified value is associated 150 | * @param value value expected to be associated with the specified key 151 | * @return a new hash after the entry is removed 152 | * @throws UnsupportedOperationException if the {@code remove} operation 153 | * is not supported by this hash 154 | * @throws ClassCastException if the key or value is of an inappropriate 155 | * type for this hash 156 | * @throws NullPointerException if the specified key or value is null, 157 | * and this map does not permit null keys or values 158 | */ 159 | Hash remove(K key, V value); 160 | 161 | /** 162 | * Returns a Seq of keys of the given value. 163 | * 164 | * @param value the value 165 | * @return the collection of keys whose value is the given value 166 | */ 167 | Seq keysOf(V value); 168 | 169 | /** 170 | * Replaces the entry for the specified key only if it is 171 | * currently mapped to some value. 172 | * 173 | * @param key key with which the specified value is associated 174 | * @param value value to be associated with the specified key 175 | * @return a new hash after the entry is replaced 176 | * @throws ClassCastException if the class of the specified key or value 177 | * prevents it from being stored in this hash 178 | * @throws NullPointerException if the specified key or value is null, 179 | * and this hash does not permit null keys or values 180 | * @throws IllegalArgumentException if some property of the specified key 181 | * or value prevents it from being stored in this hash 182 | */ 183 | Hash replace(K key, V value); 184 | 185 | /** 186 | * Replaces the entry for the specified key only if currently 187 | * mapped to the specified value. 188 | * 189 | * @param key key with which the specified value is associated 190 | * @param oldValue value expected to be associated with the specified key 191 | * @param newValue value to be associated with the specified key 192 | * @return a new hash after the entry is replaced 193 | * @throws UnsupportedOperationException if the {@code put} operation 194 | * is not supported by this hash 195 | * @throws ClassCastException if the class of a specified key or value 196 | * prevents it from being stored in this hash 197 | * @throws NullPointerException if a specified key or newValue is null, 198 | * and this hash does not permit null keys or values 199 | * @throws NullPointerException if oldValue is null and this hash does not 200 | * permit null values 201 | * @throws IllegalArgumentException if some property of a specified key 202 | * or value prevents it from being stored in this hash 203 | */ 204 | Hash replace(K key, V oldValue, V newValue); 205 | 206 | /** 207 | * Replaces each entry's value with the result of invoking the given 208 | * function on that entry until all entries have been processed or the 209 | * function throws an exception. Exceptions thrown by the function are 210 | * relayed to the caller. 211 | * 212 | * @param function the function to apply to each entry 213 | * @return a new hash after all the entries are replaced 214 | * @throws UnsupportedOperationException if the {@code set} operation 215 | * is not supported by this hash's entry set iterator. 216 | * @throws ClassCastException if the class of a replacement value 217 | * prevents it from being stored in this hash 218 | * @throws NullPointerException if the specified function is null, or the 219 | * specified replacement value is null, and this hash does not permit null 220 | * values 221 | * @throws ClassCastException if a replacement value is of an inappropriate 222 | * type for this hash 223 | * @throws NullPointerException if function or a replacement value is null, 224 | * and this hash does not permit null keys or values 225 | * @throws IllegalArgumentException if some property of a replacement value 226 | * prevents it from being stored in this hash 227 | * @throws ConcurrentModificationException if an entry is found to be 228 | * removed during iteration 229 | */ 230 | Hash replaceAll(BiFunction function); 231 | 232 | /** 233 | * Returns the number of the specified value in this hash. 234 | * @param value the value to count 235 | * @return the number of the specified value in this hash 236 | */ 237 | int count(V value); 238 | 239 | /** 240 | * Returns the number of entries which satisfy the condition. 241 | * 242 | * @param condition the condition used to filter entries by passing the key and the value, 243 | * returns true if the entry satisfies the condition, otherwise returns false. 244 | * @return the number of entries which satisfy the condition 245 | * @throws NullPointerException if condition is null 246 | */ 247 | int countIf(BiPredicate condition); 248 | 249 | HashMap toHashMap(); 250 | } 251 | -------------------------------------------------------------------------------- /src/test/java/com/worksap/icefig/lang/CharSeqTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.function.Consumer; 24 | 25 | import static org.junit.Assert.*; 26 | 27 | /** 28 | * Created by lijunxiao on 7/27/15. 29 | */ 30 | public class CharSeqTest { 31 | @Test 32 | public void testSubSeq() { 33 | CharSeq seq = CharSeq.of("Father Charles gets down and ends battle."); 34 | assertEquals(CharSeq.of("Father"), seq.subSeq(0, 6)); 35 | assertEquals(CharSeq.of("Charles "), seq.subSeq(7, 15)); 36 | assertEquals(CharSeq.of("Charles gets down and ends battle."), seq.subSeq(7, 41)); 37 | assertEquals(CharSeq.of("."), seq.subSeq(40)); 38 | assertEquals(CharSeq.of(""), seq.subSeq(41)); 39 | assertEquals(CharSeq.of("Father Charles gets down and ends battle."), seq.subSeq(0)); 40 | } 41 | 42 | @Test 43 | public void testLength() { 44 | CharSeq seq = CharSeq.of("The quick brown fox jumps over a lazy dog"); 45 | assertEquals(41, seq.length()); 46 | 47 | CharSeq emptySeq = CharSeq.of(""); 48 | assertEquals(0, emptySeq.length()); 49 | } 50 | 51 | @Test 52 | public void testCapitalize() { 53 | CharSeq seq = CharSeq.of("the Quick Brown FoX JumpS Over A lazy dog"); 54 | assertEquals(CharSeq.of("The quick brown fox jumps over a lazy dog"), seq.capitalize()); 55 | 56 | assertEquals(CharSeq.of(""), CharSeq.of("").capitalize()); 57 | } 58 | 59 | @Test 60 | public void testToLowerCase() { 61 | assertEquals(CharSeq.of("the quick brown fox jumps over a lazy dog"), 62 | CharSeq.of("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG").toLowerCase()); 63 | 64 | assertEquals(CharSeq.of(""), CharSeq.of("").toLowerCase()); 65 | } 66 | 67 | @Test 68 | public void testConcatAndPrepend() { 69 | CharSeq seq = CharSeq.of("I'm a rich man"); 70 | assertEquals(CharSeq.of("I'm a rich man, am I?"), seq.concat(CharSeq.of(", am I?"))); 71 | assertEquals(CharSeq.of("I'm a rich man, am I? Of course!"), seq.concat(", am I? Of course!")); 72 | assertEquals(CharSeq.of("Do you think I'm a rich man?"), seq.concat("?").prepend(CharSeq.of("Do you think "))); 73 | assertEquals(CharSeq.of("Don't you think I'm a rich man?"), seq.concat("?").prepend("Don't you think ")); 74 | } 75 | 76 | @Test 77 | public void testReverse() { 78 | assertEquals(CharSeq.of(""), CharSeq.of("").reverse()); 79 | assertEquals(CharSeq.of("was i tac a ro rac a ti saw"), CharSeq.of("was it a car or a cat i saw").reverse()); 80 | } 81 | 82 | @Test 83 | public void testSwapcase() { 84 | assertEquals(CharSeq.of(""), CharSeq.of("").swapcase()); 85 | assertEquals(CharSeq.of("tHe QuIck bRown fOx jumpS ovEr a laZy DoG"), 86 | CharSeq.of("ThE qUiCK BrOWN FoX JUMPs OVeR A LAzY dOg").swapcase()); 87 | } 88 | 89 | @Test 90 | public void testSplit() { 91 | assertArrayEquals(new CharSeq[]{CharSeq.of("021"), CharSeq.of("50242132")}, 92 | CharSeq.of("021 50242132").split("[- ]").toArray()); 93 | assertArrayEquals(new CharSeq[]{CharSeq.of("021"), CharSeq.of("50242132")}, 94 | CharSeq.of("021-50242132").split("[- ]").toArray()); 95 | assertArrayEquals(new CharSeq[]{CharSeq.of("021_50242132")}, 96 | CharSeq.of("021_50242132").split("[- ]").toArray()); 97 | } 98 | 99 | @Test 100 | public void testStartsWithAndEndsWith() { 101 | assertTrue(CharSeq.of("021 50242132").startsWith(CharSeq.of("021"))); 102 | assertFalse(CharSeq.of("021 50242132").startsWith(CharSeq.of("021 502421321"))); 103 | assertFalse(CharSeq.of("021 50242132").endsWith(CharSeq.of("232"))); 104 | } 105 | 106 | @Test 107 | public void testTrim() { 108 | assertEquals(CharSeq.of("With prefix blanking"), CharSeq.of(" With prefix blanking").trim()); 109 | assertEquals(CharSeq.of("With no blanking"), CharSeq.of("With no blanking").trim()); 110 | assertEquals(CharSeq.of("With suffix blanking"), CharSeq.of("With suffix blanking ").trim()); 111 | assertEquals(CharSeq.of("With both side blanking"), CharSeq.of(" With both side blanking ").trim()); 112 | } 113 | 114 | @Test 115 | public void testScan() { 116 | CharSeq seq = CharSeq.of("ATE@ShangHai Works Applications"); 117 | assertArrayEquals(new CharSeq[]{CharSeq.of("ATE"), CharSeq.of("ShangHai"), CharSeq.of("Works"), CharSeq.of("Applications")}, seq.scan("\\w+").toArray()); 118 | assertArrayEquals(new CharSeq[]{CharSeq.of("ATE@ShangHai"), CharSeq.of("Works"), CharSeq.of("Applications")}, seq.scan("[\\w@]+").toArray()); 119 | } 120 | 121 | @Test 122 | public void testPartition() { 123 | CharSeq seq = CharSeq.of("var sample = 'Good good study, day day up.'"); 124 | assertArrayEquals(new CharSeq[]{CharSeq.of("var sample "), CharSeq.of("="), CharSeq.of(" 'Good good study, day day up.'")}, 125 | seq.partition("=").toArray()); 126 | 127 | CharSeq seq1 = CharSeq.of("_word"); 128 | assertArrayEquals(new CharSeq[]{CharSeq.of(""), CharSeq.of("_word"), CharSeq.of("")}, 129 | seq1.partition("\\w+").toArray()); 130 | assertArrayEquals(new CharSeq[]{CharSeq.of(""), CharSeq.of(""), CharSeq.of("_word")}, 131 | seq1.partition("\\W+").toArray()); 132 | 133 | CharSeq seq2 = CharSeq.of("hello"); 134 | assertArrayEquals(new CharSeq[]{CharSeq.of("he"), CharSeq.of("l"), CharSeq.of("lo")}, 135 | seq2.partition("l").toArray()); 136 | assertArrayEquals(new CharSeq[]{CharSeq.of("hel"), CharSeq.of("l"), CharSeq.of("o")}, 137 | seq2.rPartition("l").toArray()); 138 | assertArrayEquals(new CharSeq[]{CharSeq.of(""), CharSeq.of(""), CharSeq.of("hello")}, 139 | seq2.rPartition("no").toArray()); 140 | } 141 | 142 | @Test 143 | public void testGetLines() { 144 | CharSeq seq = CharSeq.of("凤凰台上凤凰游,凤去台空江自流。\n" + 145 | "吴宫花草埋幽径,晋代衣冠成古丘。\n" + 146 | "三山半落青天外,二水中分白鹭洲。\r\n" + 147 | "总为浮云能蔽日,长安不见使人愁。"); 148 | assertArrayEquals(new CharSeq[]{ 149 | CharSeq.of("凤凰台上凤凰游,凤去台空江自流。"), 150 | CharSeq.of("吴宫花草埋幽径,晋代衣冠成古丘。"), 151 | CharSeq.of("三山半落青天外,二水中分白鹭洲。"), 152 | CharSeq.of("总为浮云能蔽日,长安不见使人愁。") 153 | }, seq.eachLine().toArray()); 154 | } 155 | 156 | @Test 157 | public void testCompare() { 158 | assertTrue(CharSeq.of("Good Boy").compareTo(CharSeq.of("good boy")) < 0); 159 | assertTrue(CharSeq.of("Good Boy").compareToIgnoreCase(CharSeq.of("good boy")) == 0); 160 | } 161 | 162 | @Test 163 | public void testReplace() { 164 | CharSeq seq = CharSeq.of("PhoneNumbers: 021-55831183, 010-55131123, 020-11239901"); 165 | assertEquals(CharSeq.of("PhoneNumbers: 025-55831183, 010-55131123, 020-11239901"), 166 | seq.replaceFirst("\\d{3}-", CharSeq.of("025-"))); 167 | assertEquals(CharSeq.of("PhoneNumbers: 025-55831183, 025-55131123, 025-11239901"), 168 | seq.replaceAll("\\d{3}-", CharSeq.of("025-"))); 169 | } 170 | 171 | @Test 172 | public void testMatches() { 173 | String regex = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*" + 174 | "@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; 175 | assertTrue(CharSeq.of("ljxnegod@hotmail.com").matches(regex)); 176 | assertFalse(CharSeq.of("ljxnegod@1").matches(regex)); 177 | assertFalse(CharSeq.of("ljxnegod@1 ").matches(regex)); 178 | assertFalse(CharSeq.of("ljxnegod@1..com.cn").matches(regex)); 179 | } 180 | 181 | @Test 182 | public void testEachLines() { 183 | CharSeq seq = CharSeq.of("凤凰台上凤凰游,凤去台空江自流。\r\n" + 184 | "吴宫花草埋幽径,晋代衣冠成古丘。\n" + 185 | "三山半落青天外,二水中分白鹭洲。\n" + 186 | "总为浮云能蔽日,长安不见使人愁。"); 187 | CharSeq seq2 = CharSeq.of("凤凰台上凤凰游,凤去台空江自流。\r\n" + 188 | "三山半落青天外,二水中分白鹭洲。\n"); 189 | 190 | MutableSeq result = Seqs.newMutableSeq(); 191 | seq.forEachLine((Consumer) result::appendInPlace); 192 | assertEquals(seq.eachLine(), result); 193 | 194 | MutableSeq result2 = Seqs.newMutableSeq(); 195 | seq.forEachLine((c, i) -> { 196 | if (i % 2 == 0) { 197 | result2.appendInPlace(c); 198 | } 199 | }); 200 | assertEquals(seq2.eachLine(), result2); 201 | } 202 | 203 | @Test 204 | public void testEachChar() { 205 | CharSeq seq = CharSeq.of("Albert"); 206 | Character[] increased = new Character[]{'B', 'm', 'c', 'f', 's', 'u'}; 207 | List chars = new ArrayList<>(); 208 | seq.forEachChar(ch -> chars.add((char) (ch + 1))); 209 | 210 | assertArrayEquals(increased, chars.toArray()); 211 | seq.forEachChar((ch, index) -> assertEquals(new Character((char) (ch + 1)), increased[index])); 212 | } 213 | 214 | @Test 215 | public void testEachByte() { 216 | String einstein = "Einstein"; 217 | CharSeq seq = CharSeq.of(einstein); 218 | MutableSeq bytes = Seqs.newMutableSeq(); 219 | seq.forEachByte((Consumer) bytes::appendInPlace); 220 | seq.forEachByte((b, i) -> assertEquals(new Character((char) (b.byteValue())), seq.charAt(i))); 221 | } 222 | 223 | @Test 224 | public void testEachCodePoint() { 225 | CharSeq cs = CharSeq.of("hello\u0639"); 226 | MutableSeq codePoints = Seqs.newMutableSeq(); 227 | cs.forEachCodePoint(codePoints::appendInPlace); 228 | 229 | assertEquals(6, cs.length()); 230 | assertEquals(6, cs.eachCodePoint().size()); 231 | assertEquals(Seqs.newMutableSeq(104, 101, 108, 108, 111, 1593), codePoints); 232 | assertEquals(Seqs.newMutableSeq(), CharSeq.of("").eachCodePoint()); 233 | Helpers.assertThrows(NullPointerException.class, () -> cs.forEachCodePoint(null)); 234 | } 235 | 236 | @Test 237 | public void testEquals() { 238 | CharSeq cs = CharSeq.of("Hello World!"); 239 | assertFalse(cs.equals(null)); 240 | assertFalse(cs.equals("Hello World!")); 241 | assertTrue(cs.equals(CharSeq.of("Hello World!"))); 242 | assertFalse(cs.equals(CharSeq.of("Hello!"))); 243 | } 244 | 245 | @Test 246 | public void testContainSubSeq() { 247 | assertTrue(CharSeq.of("BBC ABCDAB ABCDABCDABDE") 248 | .containsSubSeq("ABCDABD")); 249 | assertTrue(CharSeq.of("BBC ABCDAB ABCDABCDABDE") 250 | .containsSubSeq(CharSeq.of("ABCDABD"))); 251 | assertFalse(CharSeq.of("BBC ABCDAB ABCDABCDABDE") 252 | .containsSubSeq("ABCDABA")); 253 | assertFalse(CharSeq.of("BBC ABCDAB ABCDABCDABDE") 254 | .containsSubSeq(CharSeq.of("ABCDABA"))); 255 | assertFalse(CharSeq.of("A") 256 | .containsSubSeq("B")); 257 | assertFalse(CharSeq.of("") 258 | .containsSubSeq("B")); 259 | assertEquals(-1, CharSeq.of("").lastIndexOfSubSeq(CharSeq.of("A"))); 260 | assertEquals(-1, CharSeq.of("AB").lastIndexOfSubSeq("CB")); 261 | assertEquals(-1, CharSeq.of("AAB").lastIndexOfSubSeq("CB")); 262 | assertEquals(0, CharSeq.of("A").lastIndexOfSubSeq(CharSeq.of(""))); 263 | assertTrue(CharSeq.of("A") 264 | .containsSubSeq(CharSeq.of(""))); 265 | assertTrue(CharSeq.of("ABC") 266 | .containsSubSeq("ABC")); 267 | 268 | Helpers.assertThrows(NullPointerException.class, () -> CharSeq.of("ABC").containsSubSeq((String) null)); 269 | Helpers.assertThrows(NullPointerException.class, () -> CharSeq.of("ABC").containsSubSeq((CharSeq) null)); 270 | 271 | assertEquals(15, CharSeq.of("BBC ABCDAB ABCDABCDABDE") 272 | .indexOfSubSeq("ABCDABD")); 273 | assertEquals(15, CharSeq.of("BBC ABCDAB ABCDABCDABDE") 274 | .lastIndexOfSubSeq("ABCDABD")); 275 | 276 | assertEquals(1, CharSeq.of("31212123") 277 | .indexOfSubSeq("1212")); 278 | assertEquals(3, CharSeq.of("31212123") 279 | .lastIndexOfSubSeq("1212")); 280 | 281 | Helpers.assertThrows(NullPointerException.class, () -> CharSeq.of("ABC").indexOfSubSeq((String) null)); 282 | Helpers.assertThrows(NullPointerException.class, () -> CharSeq.of("ABC").lastIndexOfSubSeq((String) null)); 283 | Helpers.assertThrows(NullPointerException.class, () -> CharSeq.of("ABC").indexOfSubSeq((CharSeq) null)); 284 | Helpers.assertThrows(NullPointerException.class, () -> CharSeq.of("ABC").lastIndexOfSubSeq((CharSeq) null)); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/test/java/com/worksap/icefig/lang/HashTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import com.worksap.icefig.lang.*; 20 | import org.junit.Test; 21 | 22 | import java.lang.reflect.Constructor; 23 | import java.lang.reflect.Modifier; 24 | import java.util.HashMap; 25 | import java.util.Objects; 26 | 27 | import static org.junit.Assert.*; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertTrue; 30 | 31 | /** 32 | * Created by lijunxiao on 8/6/15. 33 | */ 34 | public class HashTest { 35 | @Test 36 | public void testConstruction() { 37 | MutableHash hash = Hashes.newMutableHash(); 38 | assertTrue(hash.size() == 0); 39 | assertTrue(hash.isEmpty()); 40 | 41 | hash.putInPlace("23", "Micheal Jordan"); 42 | hash.putInPlace("24", "Kobe Bryant"); 43 | assertEquals(hash.size(), 2); 44 | assertEquals(hash.get("24"), "Kobe Bryant"); 45 | assertNull(hash.get("25")); 46 | 47 | hash.putInPlace("1", "Allen Iverson"); 48 | assertEquals(hash.size(), 3); 49 | hash.putIfAbsentInPlace("1", "Tracy McGrady"); 50 | assertEquals(hash.get("1"), "Allen Iverson"); 51 | hash.putInPlace("1", "Tracy McGrady"); 52 | assertEquals(hash.get("1"), "Tracy McGrady"); 53 | 54 | hash.removeInPlace("23"); 55 | assertEquals(hash.size(), 2); 56 | assertNull(hash.get("23")); 57 | assertEquals(0, hash.clear().size()); 58 | assertEquals(Hashes.newMutableHash(), hash); 59 | 60 | Hash hash1 = Hashes.newHash().put("3", "Chris Paul").put("30", "Stephen Curry"); 61 | assertEquals(2, hash1.size()); 62 | assertEquals(3, hash1.put("7", "Carmelo Anthony").size()); 63 | assertEquals(2, hash1.size()); 64 | assertEquals("Chris Paul", hash1.putIfAbsent("3", "Li").get("3")); 65 | assertEquals(1, hash1.remove("3").size()); 66 | assertEquals(null, hash1.remove("3").get("3")); 67 | assertEquals("Chris Paul", hash1.get("3")); 68 | 69 | HashMap map = new HashMap<>(); 70 | map.put("key", "value"); 71 | Hash hash2 = Hashes.newHash().put("key", "value"); 72 | assertEquals(map, hash2.toHashMap()); 73 | } 74 | 75 | @Test 76 | public void testContainsAny() { 77 | MutableHash hash = Hashes.newMutableHash(new HashMap<>()); 78 | hash.putInPlace(1, 2).putInPlace(2, 4).putInPlace(5, 1); 79 | 80 | assertTrue(hash.containsAny((k, v) -> k + v == 3)); 81 | assertTrue(hash.containsAny((k, v) -> k + v == 6)); 82 | assertFalse(hash.containsAny((k, v) -> k + v == 5)); 83 | Helpers.assertThrows(NullPointerException.class, () -> hash.containsAny(null)); 84 | } 85 | 86 | @Test 87 | public void testInvert() { 88 | Hash hash = 89 | Hashes.newHash(new HashMap<>()).put("en", "British English").put("ja", "Japanese"). 90 | put("zh", "Chinese").put("zh-CN", "Chinese"); 91 | 92 | Hash invertedHash = hash.invert(); 93 | assertEquals(invertedHash.size(), 3); 94 | assertEquals(invertedHash.get("British English"), "en"); 95 | assertEquals(invertedHash.get("Japanese"), "ja"); 96 | assertEquals(invertedHash.get("Chinese"), "zh"); 97 | } 98 | 99 | @Test 100 | public void testReject(){ 101 | Hash hash = Hashes.newHash().put(1, 2).put(2, 4).put(5, 1); 102 | Hash rejected = Hashes.newHash().put(2, 4).put(5, 1); 103 | 104 | assertTrue(rejected.equals(hash.reject((k, v) -> k + v != 6))); 105 | assertEquals(Hashes.newHash(), hash.reject((k, v) -> !Objects.equals(k, v))); 106 | assertNotEquals(Hashes.newHash(), hash); 107 | Helpers.assertThrows(NullPointerException.class, () -> hash.reject(null)); 108 | } 109 | 110 | @Test 111 | public void testFilter(){ 112 | Hash hash = Hashes.newHash().put(1, 2).put(2, 4).put(5, 1); 113 | Hash origin = Hashes.newHash().put(1, 2).put(2, 4).put(5, 1); 114 | assertEquals(origin, hash); 115 | 116 | Hash filtered = Hashes.newHash().put(1, 2); 117 | assertTrue(filtered.equals(hash.filter((k, v) -> k + v != 6))); 118 | assertFalse(filtered.equals(hash)); 119 | assertEquals(origin, hash); 120 | assertEquals(Hashes.newHash(), hash.filter(Objects::equals)); 121 | assertNotEquals(Hashes.newHash(), hash); 122 | assertEquals(origin, hash); 123 | Helpers.assertThrows(NullPointerException.class, () -> hash.filter(null)); 124 | } 125 | 126 | @Test 127 | public void testRejectInplace() { 128 | MutableHash hash = Hashes.newMutableHash(); 129 | hash.putInPlace(1, 2).putInPlace(2, 4).putInPlace(5, 1); 130 | assertEquals(2, hash.rejectInPlace((k, v) -> k > 1 && v > 1).size()); 131 | assertEquals(2, hash.size()); 132 | assertEquals(null, hash.get(2)); 133 | assertEquals(2, hash.rejectInPlace(Objects::equals).size()); 134 | assertEquals(Hashes.newHash(), hash.rejectInPlace((k, v) -> !Objects.equals(k, v))); 135 | Helpers.assertThrows(NullPointerException.class, () -> hash.rejectInPlace(null)); 136 | } 137 | 138 | @Test 139 | public void testFilterInplace() { 140 | MutableHash hash = Hashes.newMutableHash(); 141 | hash.putInPlace(1, 2).putInPlace(2, 4).putInPlace(5, 1); 142 | assertEquals(3, hash.size()); 143 | assertEquals(2, hash.filterInPlace((k, v) -> !(k > 1 && v > 1)).size()); 144 | assertEquals(null, hash.get(2)); 145 | assertEquals(2, hash.filterInPlace((k, v) -> !Objects.equals(k, v)).size()); 146 | assertEquals(Hashes.newHash(), hash.filterInPlace(Objects::equals)); 147 | Helpers.assertThrows(NullPointerException.class, () -> hash.filterInPlace(null)); 148 | } 149 | 150 | @Test 151 | public void testKeysOf() { 152 | Hash hash = Hashes.newHash().put(1, 2).put(2, 4).put(3, 2).put(4, null).put(5, 1); 153 | Seq keys = hash.keysOf(2); 154 | keys.sort(Integer::compare); 155 | assertEquals(Seqs.newSeq(1, 3), keys); 156 | assertEquals(Seqs.newSeq(2), hash.keysOf(4)); 157 | assertEquals(Seqs.newSeq(), hash.keysOf(3)); 158 | assertEquals(Seqs.newSeq(4), hash.keysOf(null)); 159 | } 160 | 161 | @Test 162 | public void testMerge() { 163 | Hash hash1 = Hashes.newHash().put(1, 2).put(2, 4); 164 | Hash origin1 = Hashes.newHash().put(1, 2).put(2, 4); 165 | Hash hash2 = Hashes.newHash().put(3, 2).put(2, 3); 166 | Hash origin2 = Hashes.newHash().put(3, 2).put(2, 3); 167 | 168 | Hash merged = Hashes.newHash().put(1, 2).put(2, 3).put(3, 2); 169 | 170 | assertEquals(merged, hash1.merge(hash2)); 171 | assertEquals(origin1, hash1); 172 | assertEquals(origin2, hash2); 173 | 174 | merged = merged.put(2, 4); 175 | assertEquals(merged, hash2.merge(hash1)); 176 | assertEquals(origin1, hash1); 177 | assertEquals(origin2, hash2); 178 | assertEquals(origin1, hash1.merge(Hashes.newHash())); 179 | assertEquals(origin1, Hashes.newHash().merge(hash1)); 180 | 181 | assertEquals(origin1, hash1.merge(null)); 182 | 183 | hash1 = hash1.put(null, 1); 184 | merged = merged.put(null, 1); 185 | merged = merged.put(2, 3); 186 | assertEquals(merged, hash1.merge(hash2)); 187 | } 188 | 189 | @Test 190 | public void testMergeInplace() { 191 | MutableHash hash1 = Hashes.newMutableHash(); 192 | hash1.putInPlace(1, 2).putInPlace(2, 4); 193 | MutableHash origin1 = Hashes.newMutableHash(); 194 | origin1.putInPlace(1, 2).putInPlace(2, 4); 195 | 196 | MutableHash hash2 = Hashes.newMutableHash(); 197 | hash2.putInPlace(3, 2).putInPlace(2, 3); 198 | MutableHash origin2 = Hashes.newMutableHash(); 199 | origin2.putInPlace(3, 2).putInPlace(2, 3); 200 | 201 | MutableHash merged = Hashes.newMutableHash(); 202 | merged.putInPlace(1, 2).putInPlace(2, 3).putInPlace(3, 2); 203 | 204 | assertEquals(merged, hash1.mergeInPlace(hash2)); 205 | assertEquals(merged, hash1); 206 | assertEquals(origin2, hash2); 207 | 208 | hash1 = Hashes.newMutableHash(); 209 | hash1.putInPlace(1, 2).putInPlace(2, 4); 210 | merged.putInPlace(2, 4); 211 | assertEquals(merged, hash2.mergeInPlace(hash1)); 212 | assertEquals(origin1, hash1); 213 | assertEquals(merged, hash2); 214 | 215 | hash2 = Hashes.newMutableHash(); 216 | hash2.putInPlace(3, 2).putInPlace(2, 3); 217 | assertEquals(origin1, hash1.merge(Hashes.newHash())); 218 | assertEquals(origin1, Hashes.newHash().merge(hash1)); 219 | 220 | assertEquals(origin1, hash1.mergeInPlace(null)); 221 | 222 | hash1.putInPlace(null, 1); 223 | merged.putInPlace(null, 1); 224 | merged.putInPlace(2, 3); 225 | assertEquals(merged, hash1.mergeInPlace(hash2)); 226 | } 227 | 228 | @Test 229 | public void testKeysValues() { 230 | Hash hash = Hashes.newHash(); 231 | assertEquals(Seqs.newSeq(), hash.keys()); 232 | assertEquals(Seqs.newSeq(), hash.values()); 233 | 234 | hash = hash.put(1, null).put(null, 1); 235 | assertEquals(2, hash.keys().size()); 236 | assertTrue(hash.keys().contains(null)); 237 | assertTrue(hash.keys().contains(1)); 238 | assertEquals(2, hash.values().size()); 239 | assertTrue(hash.values().contains(null)); 240 | assertTrue(hash.values().contains(1)); 241 | assertTrue(hash.containsKey(1)); 242 | assertTrue(hash.containsKey(null)); 243 | assertTrue(hash.containsValue(1)); 244 | assertTrue(hash.containsValue(null)); 245 | assertFalse(hash.containsValue(2)); 246 | assertFalse(hash.containsKey(2)); 247 | } 248 | 249 | @Test 250 | public void testEquals() { 251 | Hash hash = Hashes.newHash().put(1, 2).put(3, 4); 252 | Hash hash2 = hash; 253 | Hash hash3 = Hashes.newHash().put(1, 2).put(3, 4); 254 | assertTrue(hash.equals(hash2)); 255 | assertFalse(hash.equals(null)); 256 | assertTrue(hash.equals(hash3)); 257 | } 258 | 259 | @Test 260 | public void testPrivateConstructor() throws Exception { 261 | Constructor constructor = Hashes.class.getDeclaredConstructor(); 262 | assertTrue("Constructor is not private", Modifier.isPrivate(constructor.getModifiers())); 263 | 264 | constructor.setAccessible(true); 265 | constructor.newInstance(); 266 | } 267 | 268 | @Test 269 | public void testRemove() { 270 | Hash hash = Hashes.newHash().put(1, 2).put(3, 4); 271 | assertEquals(2, hash.size()); 272 | 273 | Hash another = hash.remove(1); 274 | assertEquals(2, hash.size()); 275 | assertEquals(1, another.size()); 276 | 277 | another = hash.remove(1, 3); 278 | assertEquals(2, hash.size()); 279 | assertEquals(2, another.size()); 280 | 281 | another = hash.remove(2, 2); 282 | assertEquals(2, hash.size()); 283 | assertEquals(2, another.size()); 284 | 285 | another = hash.remove(1, 2); 286 | assertEquals(2, hash.size()); 287 | assertEquals(1, another.size()); 288 | 289 | MutableHash mutableHash = Hashes.newMutableHash().putInPlace(1, 2).putInPlace(3, 4); 290 | assertEquals(2, mutableHash.size()); 291 | 292 | MutableHash anotherMutableHash = mutableHash.removeInPlace(1); 293 | assertEquals(1, mutableHash.size()); 294 | assertEquals(1, anotherMutableHash.size()); 295 | mutableHash.putInPlace(1, 2); 296 | 297 | anotherMutableHash = mutableHash.removeInPlace(1, 3); 298 | assertEquals(2, mutableHash.size()); 299 | assertEquals(2, anotherMutableHash.size()); 300 | 301 | anotherMutableHash = mutableHash.removeInPlace(2, 2); 302 | assertEquals(2, mutableHash.size()); 303 | assertEquals(2, anotherMutableHash.size()); 304 | 305 | anotherMutableHash = mutableHash.removeInPlace(1, 2); 306 | assertEquals(1, mutableHash.size()); 307 | assertEquals(1, anotherMutableHash.size()); 308 | } 309 | 310 | @Test 311 | public void testReplace() { 312 | Hash hash = Hashes.newHash().put(1, 2).put(3, 4); 313 | Hash another = hash.replace(1, 3); 314 | assertEquals(new Integer(2), hash.get(1)); 315 | assertEquals(new Integer(3), another.get(1)); 316 | 317 | another = hash.replace(4, 3); 318 | assertNull(hash.get(4)); 319 | assertNull(another.get(4)); 320 | 321 | another = hash.replace(1, 2, 10); 322 | assertEquals(new Integer(2), hash.get(1)); 323 | assertEquals(new Integer(10), another.get(1)); 324 | 325 | another = hash.replace(1, 3, 11); 326 | assertEquals(new Integer(2), hash.get(1)); 327 | assertEquals(new Integer(2), another.get(1)); 328 | 329 | another = hash.replaceAll((k, v) -> k + v); 330 | assertEquals(new Integer(2), hash.get(1)); 331 | assertEquals(new Integer(3), another.get(1)); 332 | 333 | hash = hash.put(null, null); 334 | another = hash.replace(null, 1); 335 | assertEquals(null, hash.get(null)); 336 | assertEquals(new Integer(1), another.get(null)); 337 | 338 | another = hash.replace(null, null, 1); 339 | assertEquals(null, hash.get(null)); 340 | assertEquals(new Integer(1), another.get(null)); 341 | 342 | MutableHash mutableHash = Hashes.newMutableHash().putInPlace(1, 2).putInPlace(3, 4); 343 | MutableHash anotherMutableHash = mutableHash.replaceInPlace(1, 3); 344 | assertEquals(new Integer(3), mutableHash.get(1)); 345 | assertEquals(new Integer(3), anotherMutableHash.get(1)); 346 | 347 | anotherMutableHash = mutableHash.replaceInPlace(4, 3); 348 | assertNull(mutableHash.get(4)); 349 | assertNull(anotherMutableHash.get(4)); 350 | 351 | anotherMutableHash = mutableHash.replaceInPlace(1, 3, 10); 352 | assertEquals(new Integer(10), mutableHash.get(1)); 353 | assertEquals(new Integer(10), anotherMutableHash.get(1)); 354 | 355 | anotherMutableHash = mutableHash.replaceInPlace(1, 3, 11); 356 | assertEquals(new Integer(10), mutableHash.get(1)); 357 | assertEquals(new Integer(10), anotherMutableHash.get(1)); 358 | 359 | anotherMutableHash = mutableHash.replaceAllInPlace((k, v) -> k + v); 360 | assertEquals(new Integer(11), mutableHash.get(1)); 361 | assertEquals(new Integer(11), anotherMutableHash.get(1)); 362 | 363 | mutableHash.putInPlace(null, null); 364 | anotherMutableHash = mutableHash.replaceInPlace(null, 3); 365 | assertEquals(new Integer(3), mutableHash.get(null)); 366 | assertEquals(new Integer(3), anotherMutableHash.get(null)); 367 | 368 | anotherMutableHash = mutableHash.replaceInPlace(null, null, 3); 369 | assertEquals(new Integer(3), mutableHash.get(null)); 370 | assertEquals(new Integer(3), anotherMutableHash.get(null)); 371 | 372 | } 373 | 374 | @Test 375 | public void testCount() { 376 | Hash hash = Hashes.newHash().put(1, 2).put(2, 2).put(3, 4).put(null, null); 377 | assertEquals(1, hash.count(4)); 378 | assertEquals(2, hash.count(2)); 379 | assertEquals(0, hash.count(3)); 380 | assertEquals(1, hash.count(null)); 381 | assertEquals(2, hash.countIf((k, v) -> k != null && v < 3)); 382 | 383 | MutableHash mutableHash = Hashes.newMutableHash().putInPlace(1, 2).putInPlace(2, 2).putInPlace(3, 4); 384 | assertEquals(1, mutableHash.count(4)); 385 | assertEquals(2, mutableHash.count(2)); 386 | assertEquals(0, mutableHash.count(3)); 387 | assertEquals(2, mutableHash.countIf((k, v) -> v < 3)); 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/SeqImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.*; 20 | import java.util.function.*; 21 | 22 | /** 23 | * The implementation of Seq and MutableSeq. 24 | */ 25 | class SeqImpl implements MutableSeq { 26 | 27 | private final ArrayList list; 28 | 29 | SeqImpl() { 30 | this.list = new ArrayList<>(); 31 | } 32 | 33 | SeqImpl(Collection collection) { 34 | this.list = new ArrayList<>(collection); 35 | } 36 | 37 | /** 38 | * Returns the element at index. A negative index counts from the end of self. 39 | * 40 | * @param index index of the element to return 41 | * @return the element at the specified position in this seq 42 | * @throws IndexOutOfBoundsException if the index is out of range 43 | * (index >= size() || index < -size()) 44 | */ 45 | @Override 46 | public T get(int index) { 47 | int size = size(); 48 | if (index >= size || index < -size) 49 | throw new IndexOutOfBoundsException("Index " + index + ", size " + size + ", should be within [" + (-size) + ", " + size + ")"); 50 | if (index >= 0) 51 | return list.get(index); 52 | else 53 | return list.get(size + index); 54 | } 55 | 56 | @Override 57 | public MutableSeq map(Function func) { 58 | Objects.requireNonNull(func); 59 | MutableSeq result = new SeqImpl<>(); 60 | this.forEach(i -> result.appendInPlace(func.apply(i))); 61 | return result; 62 | } 63 | 64 | @Override 65 | public MutableSeq map(BiFunction func) { 66 | Objects.requireNonNull(func); 67 | MutableSeq result = new SeqImpl<>(); 68 | this.forEach((s, i) -> result.appendInPlace(func.apply(s, i))); 69 | return result; 70 | } 71 | 72 | @Override 73 | public MutableSeq flatMap(Function> func) { 74 | Objects.requireNonNull(func); 75 | MutableSeq result = new SeqImpl<>(); 76 | this.forEach(i -> result.appendInPlace(func.apply(i))); 77 | return result; 78 | } 79 | 80 | @Override 81 | public MutableSeq flatMap(BiFunction> func) { 82 | Objects.requireNonNull(func); 83 | MutableSeq result = new SeqImpl<>(); 84 | this.forEach((s, i) -> result.appendInPlace(func.apply(s, i))); 85 | return result; 86 | } 87 | 88 | @Override 89 | public MutableSeq sample(int n) { 90 | MutableSeq shuffled = shuffle(); 91 | return shuffled.subSeq(0, Math.min(n, this.size())); 92 | } 93 | 94 | @Override 95 | public int size() { 96 | return list.size(); 97 | } 98 | 99 | @Override 100 | public Object[] toArray() { 101 | return list.toArray(); 102 | } 103 | 104 | @Override 105 | public MutableSeq shuffle() { 106 | List newList = new ArrayList<>(list); 107 | Collections.shuffle(newList); 108 | return new SeqImpl<>(newList); 109 | } 110 | 111 | @Override 112 | public MutableSeq> eachCons(int n) { 113 | if (n <= 0) { 114 | throw new IllegalArgumentException("n should be positive number!"); 115 | } 116 | MutableSeq> result = new SeqImpl<>(); 117 | for (int i = 0; i <= this.size() - n; i++) { 118 | result.appendInPlace(this.subSeq(i, i + n)); 119 | } 120 | return result; 121 | } 122 | 123 | @Override 124 | public MutableSeq sort(Comparator comparator) { 125 | List newList = new ArrayList<>(list); 126 | Collections.sort(newList, comparator); 127 | return new SeqImpl<>(newList); 128 | } 129 | 130 | @Override 131 | public MutableSeq distinct() { 132 | return new SeqImpl<>(new LinkedHashSet<>(list)); 133 | } 134 | 135 | @Override 136 | public MutableSeq append(T value) { 137 | List newList = new ArrayList<>(list); 138 | newList.add(value); 139 | return new SeqImpl<>(newList); 140 | } 141 | 142 | @Override 143 | @SafeVarargs 144 | final public MutableSeq append(T... values) { 145 | return append(Arrays.asList(values)); 146 | } 147 | 148 | @Override 149 | public MutableSeq append(Collection collection) { 150 | List newList = new ArrayList<>(list); 151 | newList.addAll(collection); 152 | return new SeqImpl<>(newList); 153 | } 154 | 155 | @Override 156 | public MutableSeq append(Seq seq) { 157 | List newList = new ArrayList<>(list); 158 | seq.forEach((Consumer) newList::add); 159 | return new SeqImpl<>(newList); 160 | } 161 | 162 | @Override 163 | public MutableSeq appendInPlace(T value) { 164 | list.add(value); 165 | return this; 166 | } 167 | 168 | @Override 169 | @SafeVarargs 170 | final public MutableSeq appendInPlace(T... values) { 171 | Collections.addAll(list, values); 172 | return this; 173 | } 174 | 175 | @Override 176 | public MutableSeq appendInPlace(Collection collection) { 177 | list.addAll(collection); 178 | return this; 179 | } 180 | 181 | @Override 182 | public MutableSeq appendInPlace(Seq seq) { 183 | list.addAll(seq.toArrayList()); 184 | return this; 185 | } 186 | 187 | @Override 188 | public MutableSeq prepend(T value) { 189 | List newList = new ArrayList<>(); 190 | newList.add(value); 191 | newList.addAll(list); 192 | return new SeqImpl<>(newList); 193 | } 194 | 195 | @Override 196 | @SafeVarargs 197 | final public MutableSeq prepend(T... values) { 198 | Objects.requireNonNull(values); 199 | return prepend(Arrays.asList(values)); 200 | } 201 | 202 | @Override 203 | public MutableSeq prepend(Collection collection) { 204 | List newList = new ArrayList<>(); 205 | newList.addAll(collection); 206 | newList.addAll(list); 207 | return new SeqImpl<>(newList); 208 | } 209 | 210 | @Override 211 | public MutableSeq prepend(Seq seq) { 212 | List newList = new ArrayList<>(); 213 | seq.forEach((Consumer) newList::add); 214 | newList.addAll(list); 215 | return new SeqImpl<>(newList); 216 | } 217 | 218 | @Override 219 | public MutableSeq prependInPlace(T value) { 220 | list.add(0, value); 221 | return this; 222 | } 223 | 224 | @Override 225 | @SafeVarargs 226 | final public MutableSeq prependInPlace(T... values) { 227 | return prependInPlace(Arrays.asList(values)); 228 | } 229 | 230 | @Override 231 | public MutableSeq prependInPlace(Collection collection) { 232 | list.addAll(0, collection); 233 | return this; 234 | } 235 | 236 | @Override 237 | public MutableSeq prependInPlace(Seq seq) { 238 | list.addAll(0, seq.toArrayList()); 239 | return this; 240 | } 241 | 242 | @Override 243 | public MutableSeq subSeq(int fromIndex, int toIndex) { 244 | return new SeqImpl<>(list.subList(fromIndex, toIndex)); 245 | } 246 | 247 | @Override 248 | public MutableSeq reject(Predicate condition) { 249 | Objects.requireNonNull(condition); 250 | List newList = new ArrayList<>(); 251 | this.forEach(e -> { 252 | if (!condition.test(e)) 253 | newList.add(e); 254 | }); 255 | return new SeqImpl<>(newList); 256 | } 257 | 258 | @Override 259 | public MutableSeq reject(BiPredicate condition) { 260 | Objects.requireNonNull(condition); 261 | List newList = new ArrayList<>(); 262 | this.forEach((e, i) -> { 263 | if (!condition.test(e, i)) 264 | newList.add(e); 265 | }); 266 | return new SeqImpl<>(newList); 267 | } 268 | 269 | @Override 270 | public MutableSeq rejectWhile(Predicate condition) { 271 | Objects.requireNonNull(condition); 272 | int idx = 0; 273 | for (; idx < size() && condition.test(get(idx)); idx++); 274 | 275 | MutableSeq seq = new SeqImpl<>(); 276 | for (; idx < size(); idx++) { 277 | seq.appendInPlace(get(idx)); 278 | } 279 | 280 | return seq; 281 | } 282 | 283 | @Override 284 | public MutableSeq rejectWhile(BiPredicate condition) { 285 | Objects.requireNonNull(condition); 286 | int idx = 0; 287 | for (; idx < size() && condition.test(get(idx), idx); idx++); 288 | 289 | MutableSeq seq = new SeqImpl<>(); 290 | for (; idx < size(); idx++) { 291 | seq.appendInPlace(get(idx)); 292 | } 293 | 294 | return seq; 295 | } 296 | 297 | @Override 298 | public MutableSeq filter(Predicate condition) { 299 | Objects.requireNonNull(condition); 300 | List newList = new ArrayList<>(); 301 | this.forEach(e -> { 302 | if (condition.test(e)) 303 | newList.add(e); 304 | }); 305 | return new SeqImpl<>(newList); 306 | } 307 | 308 | @Override 309 | public MutableSeq filter(BiPredicate condition) { 310 | Objects.requireNonNull(condition); 311 | List newList = new ArrayList<>(); 312 | this.forEach((e, i) -> { 313 | if (condition.test(e, i)) 314 | newList.add(e); 315 | }); 316 | return new SeqImpl<>(newList); 317 | } 318 | 319 | @Override 320 | public MutableSeq filterWhile(Predicate condition) { 321 | Objects.requireNonNull(condition); 322 | MutableSeq seq = new SeqImpl<>(); 323 | for (int idx = 0; idx < size() && condition.test(get(idx)); idx++) { 324 | seq.appendInPlace(get(idx)); 325 | } 326 | return seq; 327 | } 328 | 329 | @Override 330 | public MutableSeq filterWhile(BiPredicate condition) { 331 | Objects.requireNonNull(condition); 332 | MutableSeq seq = new SeqImpl<>(); 333 | for (int idx = 0; idx < size() && condition.test(get(idx), idx); idx++) { 334 | seq.appendInPlace(get(idx)); 335 | } 336 | return seq; 337 | } 338 | 339 | @Override 340 | public MutableSeq repeat(int times) { 341 | if (times <= 0) 342 | throw new IllegalArgumentException("times must be a positive number."); 343 | List newList = new ArrayList<>(); 344 | while (times > 0) { 345 | newList.addAll(list); 346 | times--; 347 | } 348 | return new SeqImpl<>(newList); 349 | } 350 | 351 | @Override 352 | public MutableSeq> eachSlice(int n) { 353 | if (n <= 0) 354 | throw new IllegalArgumentException("n should be a positive number."); 355 | List> newList = new ArrayList<>(); 356 | int size = this.size(); 357 | for (int i = 0; i < size; i += n) { 358 | newList.add(subSeq(i, i + n > size ? size : i + n)); 359 | } 360 | return new SeqImpl<>(newList); 361 | } 362 | 363 | @Override 364 | public boolean isEmpty() { 365 | return list.isEmpty(); 366 | } 367 | 368 | @Override 369 | public MutableSeq reverse() { 370 | List newList = new ArrayList<>(); 371 | for (int i = size() - 1; i >= 0; i--) 372 | newList.add(this.get(i)); 373 | return new SeqImpl<>(newList); 374 | } 375 | 376 | 377 | private Seq indexToSeq(int[] idxs) { 378 | MutableSeq result = new SeqImpl<>(); 379 | for (int i : idxs) { 380 | result.appendInPlace(get(i)); 381 | } 382 | return result; 383 | } 384 | 385 | public void forEachCombination(int n, Consumer> action) { 386 | Objects.requireNonNull(action); 387 | if (n <= 0) { 388 | throw new IllegalArgumentException("n should be a positive number"); 389 | } 390 | if (n > size()) { 391 | return; 392 | } 393 | //Selected element indices of a valid combination 394 | int[] comb = new int[n]; 395 | 396 | //initialize first combination by the first n elements 397 | for (int i = 0; i < n; i++) { 398 | comb[i] = i; 399 | } 400 | action.accept(indexToSeq(comb)); 401 | 402 | while (comb[0] < size() - n) { 403 | for (int i = 0; ; i++) { 404 | if (i == n - 1 || comb[i + 1] - comb[i] > 1) { // find the first selected element that the next element of it is not selected 405 | comb[i]++; // make the next element selected instead 406 | // set all selected elements before i, to the beginning elements 407 | for (int j = 0; j < i; j++) { 408 | comb[j] = j; 409 | } 410 | action.accept(indexToSeq(comb)); 411 | break; 412 | } 413 | } 414 | } 415 | } 416 | 417 | public MutableSeq> eachCombination(int n) { 418 | MutableSeq> result = new SeqImpl<>(); 419 | forEachCombination(n, s -> result.appendInPlace((MutableSeq) s)); 420 | return result; 421 | } 422 | 423 | @Override 424 | public MutableSeq clear() { 425 | list.clear(); 426 | return this; 427 | } 428 | 429 | @Override 430 | public boolean contains(T t) { 431 | return list.contains(t); 432 | } 433 | 434 | @Override 435 | public MutableSeq set(int i, T t) { 436 | list.set(i, t); 437 | return this; 438 | } 439 | 440 | @Override 441 | public ArrayList toArrayList() { 442 | return list; 443 | } 444 | 445 | @Override 446 | public MutableSeq shuffleInPlace() { 447 | Collections.shuffle(list); 448 | return this; 449 | } 450 | 451 | @Override 452 | public MutableSeq distinctInPlace() { 453 | Collection collection = new LinkedHashSet<>(list); 454 | list.clear(); 455 | list.addAll(collection); 456 | return this; 457 | } 458 | 459 | @Override 460 | public MutableSeq sortInPlace(Comparator comparator) { 461 | Collections.sort(list, comparator); 462 | return this; 463 | } 464 | 465 | @Override 466 | public MutableSeq rejectInPlace(Predicate condition) { 467 | Objects.requireNonNull(condition); 468 | final Iterator each = list.iterator(); 469 | while (each.hasNext()) { 470 | if (condition.test(each.next())) { 471 | each.remove(); 472 | } 473 | } 474 | return this; 475 | } 476 | 477 | @Override 478 | public MutableSeq rejectInPlace(BiPredicate condition) { 479 | Objects.requireNonNull(condition); 480 | final Iterator each = list.iterator(); 481 | int index = 0; 482 | while (each.hasNext()) { 483 | if (condition.test(each.next(), index)) { 484 | each.remove(); 485 | } 486 | index++; 487 | } 488 | return this; 489 | } 490 | 491 | @Override 492 | public MutableSeq rejectWhileInPlace(Predicate condition) { 493 | Objects.requireNonNull(condition); 494 | final Iterator each = list.iterator(); 495 | while (each.hasNext() && condition.test(each.next())) { 496 | each.remove(); 497 | } 498 | return this; 499 | } 500 | 501 | @Override 502 | public MutableSeq rejectWhileInPlace(BiPredicate condition) { 503 | Objects.requireNonNull(condition); 504 | final Iterator each = list.iterator(); 505 | for (int idx = 0; each.hasNext() && condition.test(each.next(), idx); idx++) { 506 | each.remove(); 507 | } 508 | return this; 509 | } 510 | 511 | @Override 512 | public MutableSeq filterInPlace(Predicate condition) { 513 | Objects.requireNonNull(condition); 514 | final Iterator each = list.iterator(); 515 | while (each.hasNext()) { 516 | if (!condition.test(each.next())) { 517 | each.remove(); 518 | } 519 | } 520 | return this; 521 | } 522 | 523 | @Override 524 | public MutableSeq filterInPlace(BiPredicate condition) { 525 | Objects.requireNonNull(condition); 526 | final Iterator each = list.iterator(); 527 | int index = 0; 528 | while (each.hasNext()) { 529 | if (!condition.test(each.next(), index)) { 530 | each.remove(); 531 | } 532 | index++; 533 | } 534 | return this; 535 | } 536 | 537 | @Override 538 | public MutableSeq filterWhileInPlace(Predicate condition) { 539 | Objects.requireNonNull(condition); 540 | int posToRemove = 0; 541 | for (; posToRemove < size() && condition.test(get(posToRemove)); posToRemove++); 542 | for (int idx = size() - 1; idx >= posToRemove; idx--) { 543 | list.remove(idx); 544 | } 545 | return this; 546 | } 547 | 548 | @Override 549 | public MutableSeq filterWhileInPlace(BiPredicate condition) { 550 | Objects.requireNonNull(condition); 551 | int posToRemove = 0; 552 | for (; posToRemove < size() && condition.test(get(posToRemove), posToRemove); posToRemove++); 553 | for (int idx = size() - 1; idx >= posToRemove; idx--) { 554 | list.remove(idx); 555 | } 556 | return this; 557 | } 558 | 559 | @Override 560 | public MutableSeq repeatInPlace(int times) { 561 | if (times <= 0) 562 | throw new IllegalArgumentException("times must be a positive number."); 563 | else if (times >= 2) { 564 | times--; 565 | Collection copy = new ArrayList<>(list); 566 | while (times > 0) { 567 | list.addAll(copy); 568 | times--; 569 | } 570 | } 571 | return this; 572 | } 573 | 574 | @Override 575 | public MutableSeq compactInPlace() { 576 | list.removeIf(e -> e == null); 577 | return this; 578 | } 579 | 580 | @Override 581 | public MutableSeq reverseInPlace() { 582 | int size = size(); 583 | for (int i = 0; i < size / 2; i++) { 584 | T temp = this.get(i); 585 | this.set(i, this.get(size - 1 - i)); 586 | this.set(size - 1 - i, temp); 587 | } 588 | return this; 589 | } 590 | 591 | @Override 592 | public boolean equals(Object o) { 593 | if (this == o) return true; 594 | if (o == null || getClass() != o.getClass()) return false; 595 | SeqImpl seq = (SeqImpl) o; 596 | return Objects.equals(list, seq.list); 597 | } 598 | 599 | @Override 600 | public int hashCode() { 601 | return Objects.hash(list); 602 | } 603 | 604 | @Override 605 | public String toString() { 606 | return list.toString(); 607 | } 608 | 609 | @Override 610 | public int indexOf(T t) { 611 | return list.indexOf(t); 612 | } 613 | 614 | @Override 615 | public int lastIndexOf(T t) { 616 | return list.lastIndexOf(t); 617 | } 618 | 619 | @Override 620 | public Seq intersect(Seq seq) { 621 | Objects.requireNonNull(seq); 622 | if (seq.isEmpty()) { 623 | return new SeqImpl<>(); 624 | } 625 | 626 | HashMap map = countIndex(seq); 627 | SeqImpl result = new SeqImpl<>(); 628 | forEach(t -> { 629 | Integer count = map.get(t); 630 | if (count != null) { 631 | result.appendInPlace(t); 632 | if (count == 1) { 633 | map.remove(t); 634 | } else { 635 | map.put(t, count - 1); 636 | } 637 | } 638 | }); 639 | return result; 640 | } 641 | 642 | private HashMap countIndex(Seq seq) { 643 | HashMap map = new HashMap<>(seq.size()); 644 | seq.forEach(t -> { 645 | Integer count = map.get(t); 646 | if (count != null) { 647 | map.put(t, count + 1); 648 | } else { 649 | map.put(t, 1); 650 | } 651 | }); 652 | return map; 653 | } 654 | 655 | @Override 656 | public Seq difference(Seq seq) { 657 | Objects.requireNonNull(seq); 658 | if (seq.isEmpty()) { 659 | return new SeqImpl<>(list); 660 | } 661 | 662 | HashMap map = countIndex(seq); 663 | SeqImpl result = new SeqImpl<>(); 664 | forEach(t -> { 665 | Integer count = map.get(t); 666 | if (count != null) { 667 | if (count == 1) { 668 | map.remove(t); 669 | } else { 670 | map.put(t, count - 1); 671 | } 672 | } else { 673 | result.appendInPlace(t); 674 | } 675 | }); 676 | return result; 677 | } 678 | 679 | @Override 680 | public Seq swap(int i, int j) { 681 | MutableSeq newSeq = new SeqImpl<>(list); 682 | newSeq.swapInPlace(i, j); 683 | return newSeq; 684 | } 685 | 686 | @Override 687 | public MutableSeq swapInPlace(int i, int j) { 688 | T tmp = get(i); 689 | set(i, get(j)); 690 | set(j, tmp); 691 | return this; 692 | } 693 | 694 | @Override 695 | public Seq rotate(int distance) { 696 | MutableSeq newSeq = new SeqImpl<>(); 697 | int size = size(); 698 | if (size == 0) { 699 | return newSeq; 700 | } 701 | distance = distance % size(); 702 | if (distance < 0) { 703 | distance += size; 704 | } 705 | 706 | for (int i = 0; i < size(); i++) { 707 | newSeq.appendInPlace(get((size + i - distance) % size)); 708 | } 709 | return newSeq; 710 | } 711 | 712 | @Override 713 | public MutableSeq rotateInPlace(int distance) { 714 | int size = size(); 715 | if (size == 0 || distance % size == 0) { 716 | return this; 717 | } 718 | distance = distance % size(); 719 | if (distance < 0) { 720 | distance += size; 721 | } 722 | 723 | for (int cycleStart = 0, movedSteps = 0; movedSteps != size; cycleStart++) { 724 | T displaced = list.get(cycleStart); 725 | int i = cycleStart; 726 | do { 727 | i += distance; 728 | if (i >= size) { 729 | i -= size; 730 | } 731 | displaced = list.set(i, displaced); 732 | movedSteps++; 733 | } while (i != cycleStart); 734 | } 735 | 736 | return null; 737 | } 738 | } 739 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/CharSeq.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.Objects; 22 | import java.util.function.BiConsumer; 23 | import java.util.function.Consumer; 24 | import java.util.regex.Matcher; 25 | import java.util.regex.Pattern; 26 | 27 | /** 28 | * Elegant supplement for String in JDK 29 | */ 30 | public class CharSeq { 31 | private final String str; 32 | 33 | CharSeq(String str) { 34 | this.str = Objects.requireNonNull(str); 35 | } 36 | 37 | /** 38 | * Returns a CharSeq that contains a substring of this CharSeq's string. 39 | * The substring begins at the specified {@code beginIndex} and 40 | * extends to the character at index {@code endIndex - 1}. 41 | * Thus the length of the substring is {@code endIndex-beginIndex}. 42 | *

43 | *

44 | * Examples: 45 | *

 46 |      * CharSeq.of("hamburger").subSeq(4, 8) returns CharSeq.of("urge")
 47 |      * CharSeq.of("smiles").subSeq(1, 5) returns CharSeq.of("mile")
 48 |      * 
49 | *

50 | * 51 | * @param fromIndex the beginning index, inclusive. 52 | * @param toIndex the ending index, exclusive. 53 | * @return CharSeq with the specified substring. 54 | */ 55 | public CharSeq subSeq(int fromIndex, int toIndex) { 56 | return new CharSeq(str.substring(fromIndex, toIndex)); 57 | } 58 | 59 | /** 60 | * Returns a CharSeq that contains a substring of this CharSeq's string. 61 | * The substring begins at the specified {@code beginIndex} and 62 | * extends to the end of this string. 63 | *

64 | *

65 | * Examples: 66 | *

 67 |      * CharSeq.of("unhappy").subSeq(2) returns CharSeq.of("happy")
 68 |      * CharSeq.of("Harbison").subSeq(3) returns CharSeq.of("bison")
 69 |      * 
70 | *

71 | * 72 | * @param fromIndex the beginning index, inclusive. 73 | * @return CharSeq with the specified substring. 74 | */ 75 | public CharSeq subSeq(int fromIndex) { 76 | return this.subSeq(fromIndex, str.length()); 77 | } 78 | 79 | /** 80 | * Append string of the given CharSeq to this CharSeq 81 | * 82 | * @param another the given CharSeq 83 | * @return appended result 84 | */ 85 | public CharSeq concat(CharSeq another) { 86 | return new CharSeq(str + another.str); 87 | } 88 | 89 | /** 90 | * Append the given string to this CharSeq 91 | * 92 | * @param another the given string 93 | * @return appended result 94 | */ 95 | public CharSeq concat(String another) { 96 | return new CharSeq(str + another); 97 | } 98 | 99 | /** 100 | * Prepend string of the given CharSeq to this CharSeq 101 | * 102 | * @param another the given string 103 | * @return prepended result 104 | */ 105 | public CharSeq prepend(CharSeq another) { 106 | return new CharSeq(another.str + str); 107 | } 108 | 109 | /** 110 | * Prepend the given string to this CharSeq 111 | * 112 | * @param another the given string 113 | * @return prepended result 114 | */ 115 | public CharSeq prepend(String another) { 116 | return new CharSeq(another + str); 117 | } 118 | 119 | /** 120 | * Returns the length of string of this CharSeq. 121 | * The length is equal to the number of Unicode 122 | * code units in the string. 123 | * 124 | * @return the length 125 | */ 126 | public int length() { 127 | return str.length(); 128 | } 129 | 130 | /** 131 | * Returns {@code true} if, and only if, {@link #length()} is {@code 0}. 132 | * 133 | * @return {@code true} if {@link #length()} is {@code 0}, otherwise 134 | * {@code false} 135 | */ 136 | public boolean isEmpty() { 137 | return str.isEmpty(); 138 | } 139 | 140 | /** 141 | * Returns a copy of CharSeq with the string's first character 142 | * converted to uppercase and the remainder to lowercase. 143 | * 144 | * @return CharSeq with capitalized string 145 | */ 146 | public CharSeq capitalize() { 147 | return isEmpty() ? this : this.subSeq(0, 1).toUpperCase().concat(this.subSeq(1).toLowerCase()); 148 | } 149 | 150 | /** 151 | * Returns a copy of CharSeq with the string's characters all 152 | * converted to uppercase. 153 | * 154 | * @return 155 | */ 156 | public CharSeq toUpperCase() { 157 | return new CharSeq(str.toUpperCase()); 158 | } 159 | 160 | /** 161 | * Returns a copy of this CharSeq with characters all 162 | * converted to lowercase. 163 | * 164 | * @return 165 | */ 166 | public CharSeq toLowerCase() { 167 | return new CharSeq(str.toLowerCase()); 168 | } 169 | 170 | /** 171 | * Splits this CharSeq around matches of the given regular expression. 172 | * 173 | * @param regex Regular expression 174 | * @return 175 | */ 176 | public Seq split(String regex) { 177 | return Seqs.newSeq(str.split(regex)).map(CharSeq::new); 178 | } 179 | 180 | /** 181 | * Construct a new CharSeq with the given string 182 | * 183 | * @param str the given string 184 | * @return 185 | */ 186 | public static CharSeq of(String str) { 187 | return new CharSeq(str); 188 | } 189 | 190 | /** 191 | * Construct a new CharSeq with the given char array 192 | * 193 | * @param charArr the given char array 194 | * @return 195 | */ 196 | public static CharSeq of(char[] charArr) { 197 | return new CharSeq(new String(charArr)); 198 | } 199 | 200 | /** 201 | * Return a new CharSeq with the characters from 202 | * this CharSeq in reverse order. 203 | *

204 | *

205 | * Examples: 206 | *

207 |      * CharSeq.of("stressed").reverse() returns CharSeq.of("desserts")
208 |      * 
209 | *

210 | * 211 | * @return A new Seq 212 | */ 213 | public CharSeq reverse() { 214 | return CharSeq.of(new StringBuilder(str).reverse().toString()); 215 | } 216 | 217 | /** 218 | * Return a new CharSeq with all characters' cases toggled. 219 | *

220 | *

221 | * Examples: 222 | *

223 |      * CharSeq.of("sTrEsSed").swapcase() returns CharSeq.of("StReSsED")
224 |      * 
225 | *

226 | * 227 | * @return A new CharSeq 228 | */ 229 | public CharSeq swapcase() { 230 | char[] chars = str.toCharArray(); 231 | for (int i = 0; i < chars.length; i++) { 232 | char c = chars[i]; 233 | if (Character.isUpperCase(c)) { 234 | chars[i] = Character.toLowerCase(c); 235 | } else if (Character.isLowerCase(c)) { 236 | chars[i] = Character.toUpperCase(c); 237 | } else { 238 | chars[i] = c; 239 | } 240 | } 241 | return CharSeq.of(chars); 242 | } 243 | 244 | /** 245 | * Tests whether this CharSeq ends with the specified suffix 246 | * 247 | * @param suffix suffix 248 | * @return A boolean 249 | */ 250 | public boolean endsWith(CharSeq suffix) { 251 | return this.endsWith(suffix.str); 252 | } 253 | 254 | /** 255 | * Tests whether this CharSeq ends with the specified suffix 256 | * 257 | * @param suffix suffix 258 | * @return A boolean 259 | */ 260 | public boolean endsWith(String suffix) { 261 | return str.endsWith(suffix); 262 | } 263 | 264 | /** 265 | * Tests whether this CharSeq starts with the specified prefix 266 | * 267 | * @param prefix prefix 268 | * @return A boolean 269 | */ 270 | public boolean startsWith(CharSeq prefix) { 271 | return str.startsWith(prefix.str); 272 | } 273 | 274 | /** 275 | * Return the Character at the index i of this CharSeq 276 | * 277 | * @param i the index 278 | * @return The specified Character 279 | */ 280 | public Character charAt(int i) { 281 | return str.charAt(i); 282 | } 283 | 284 | /** 285 | * Returns a CharSeq whose value is this CharSeq, with any leading and trailing 286 | * whitespace removed. 287 | * 288 | * @return A new CharSeq with leading and trailing whitespace removed 289 | */ 290 | public CharSeq trim() { 291 | return CharSeq.of(str.trim()); 292 | } 293 | 294 | /** 295 | * Scan through this CharSeq iteratively, generate a Seq of CharSeq 296 | * with all the matching subStrings. 297 | * 298 | * @param regex The regular expression 299 | * @return A Seq of CharSeq 300 | */ 301 | public Seq scan(String regex) { 302 | Pattern pat = Pattern.compile(regex); 303 | Matcher m = pat.matcher(str); 304 | MutableSeq charSeq = Seqs.newMutableSeq(); 305 | while (m.find()) { 306 | charSeq.appendInPlace(CharSeq.of(m.group())); 307 | } 308 | return charSeq; 309 | } 310 | 311 | /** 312 | * Performs the given action for each character of the CharSeq. 313 | * 314 | * @param action Consumer with single parameter of Character 315 | * @return Self 316 | */ 317 | public CharSeq forEachChar(Consumer action) { 318 | Objects.requireNonNull(action); 319 | eachChar().forEach(action); 320 | return this; 321 | } 322 | 323 | /** 324 | * Performs the given action for each character of the CharSeq, 325 | * with additional parameter "index" as the second parameter. 326 | * 327 | * @param action BiConsumer with parameters of Character and the index 328 | * @return Self 329 | */ 330 | public CharSeq forEachChar(BiConsumer action) { 331 | Objects.requireNonNull(action); 332 | eachChar().forEach(action); 333 | return this; 334 | } 335 | 336 | /** 337 | * Performs the given action for each byte of the CharSeq. 338 | * 339 | * @param action Consumer with single parameter of Byte 340 | * @return Self 341 | */ 342 | public CharSeq forEachByte(Consumer action) { 343 | Objects.requireNonNull(action); 344 | eachByte().forEach(action); 345 | return this; 346 | } 347 | 348 | /** 349 | * Performs the given action for each byte of the CharSeq, 350 | * with additional parameter "index" as the second parameter. 351 | * 352 | * @param action BiConsumer with parameters of Byte and the index 353 | * @return Self 354 | */ 355 | public CharSeq forEachByte(BiConsumer action) { 356 | Objects.requireNonNull(action); 357 | eachByte().forEach(action); 358 | return this; 359 | } 360 | 361 | /** 362 | * Performs the given action for each line of the CharSeq. 363 | * 364 | * @param action Consumer with single parameter of CharSeq 365 | * @return Self 366 | */ 367 | public CharSeq forEachLine(Consumer action) { 368 | Objects.requireNonNull(action); 369 | Seq lines = this.eachLine(); 370 | lines.forEach(action); 371 | return this; 372 | } 373 | 374 | /** 375 | * Performs the given action for each line of the CharSeq, 376 | * with additional parameter "index" as the second parameter. 377 | * 378 | * @param action BiConsumer with parameters of CharSeq and the index 379 | * @return Self 380 | */ 381 | public CharSeq forEachLine(BiConsumer action) { 382 | Objects.requireNonNull(action); 383 | Seq lines = this.eachLine(); 384 | lines.forEach(action); 385 | return this; 386 | } 387 | 388 | /** 389 | * Split the CharSeq by the newline character and return the 390 | * result as a Seq of CharSeq. 391 | * 392 | * @return A Seq of CharSeq 393 | */ 394 | public Seq eachLine() { 395 | return this.split("\n|\r\n"); 396 | } 397 | 398 | /** 399 | * Tells whether or not this CharSeq matches the given regular expression. 400 | * 401 | * @return A boolean 402 | */ 403 | public boolean matches(String regex) { 404 | return this.str.matches(regex); 405 | } 406 | 407 | 408 | /** 409 | * Return a new CharSeq by replacing the first substring of this CharSeq 410 | * that matches the given regular expression with the given CharSeq replacement. 411 | * 412 | * @param regex The regular expression 413 | * @param replacement The replacement CharSeq 414 | * @return A new CharSeq 415 | */ 416 | public CharSeq replaceFirst(String regex, CharSeq replacement) { 417 | return this.replaceFirst(regex, replacement.str); 418 | } 419 | 420 | /** 421 | * Return a new CharSeq by replacing the first substring of this CharSeq 422 | * that matches the given regular expression with the given String replacement. 423 | * 424 | * @param regex The regular expression 425 | * @param replacement The replacement String 426 | * @return A new CharSeq 427 | */ 428 | public CharSeq replaceFirst(String regex, String replacement) { 429 | return CharSeq.of(str.replaceFirst(regex, replacement)); 430 | } 431 | 432 | /** 433 | * Return a new CharSeq by replacing each substring of this 434 | * CharSeq that matches the given regular expression with 435 | * the given CharSeq replacement. 436 | * 437 | * @param regex The regular expression 438 | * @param replacement The replacement CharSeq 439 | * @return A new CharSeq 440 | */ 441 | public CharSeq replaceAll(String regex, CharSeq replacement) { 442 | return this.replaceAll(regex, replacement.str); 443 | } 444 | 445 | /** 446 | * Return a new CharSeq by replacing each substring of this 447 | * CharSeq that matches the given regular expression with 448 | * the given String replacement. 449 | * 450 | * @param regex The regular expression 451 | * @param replacement The replacement String 452 | * @return A new CharSeq 453 | */ 454 | public CharSeq replaceAll(String regex, String replacement) { 455 | return CharSeq.of(str.replaceAll(regex, replacement)); 456 | } 457 | 458 | @Override 459 | public boolean equals(Object another) { 460 | return another instanceof CharSeq && str.equals(((CharSeq) another).str); 461 | } 462 | 463 | @Override 464 | public String toString() { 465 | return str; 466 | } 467 | 468 | /** 469 | * Compares two CharSeqs lexicographically. 470 | * 471 | * @return the value {@code 0} if the argument CharSeq is equal to 472 | * this CharSeq; a value less than {@code 0} if this CharSeq 473 | * is lexicographically less than the CharSeq argument; and a 474 | * value greater than {@code 0} if this CharSeq is 475 | * lexicographically greater than the CharSeq argument. 476 | */ 477 | public int compareTo(CharSeq another) { 478 | return str.compareTo(another.str); 479 | } 480 | 481 | /** 482 | * Compares two CharSeqs lexicographically ignore case differences. 483 | * 484 | * @return a negative integer, zero, or a positive integer as the 485 | * specified String is greater than, equal to, or less 486 | * than this String, ignoring case considerations. 487 | */ 488 | public int compareToIgnoreCase(CharSeq another) { 489 | return str.compareToIgnoreCase(another.str); 490 | } 491 | 492 | /** 493 | * Searches pattern (regex) in the CharSeq and returns 494 | * a Seq of CharSeq consists of the part before it, 495 | * the first match, and the part after it. 496 | *

497 | * If no such match is found in this CharSeq, return a Seq 498 | * of CharSeq consists two empty CharSeqs and the CharSeq itself. 499 | * 500 | * @param regex Regular Expression 501 | * @return A Seq of CharSeq 502 | */ 503 | public Seq partition(String regex) { 504 | Matcher m = Pattern.compile(regex).matcher(str); 505 | if (m.find()) { 506 | return Seqs.newSeq(CharSeq.of(str.substring(0, m.start())), 507 | CharSeq.of(m.group()), 508 | CharSeq.of(str.substring(m.end()))); 509 | } else { 510 | return Seqs.newSeq(CharSeq.of(""), CharSeq.of(""), CharSeq.of(str)); 511 | } 512 | } 513 | 514 | /** 515 | * Searches pattern (regex) in the CharSeq and returns 516 | * a Seq of CharSeq consists of the part before it, 517 | * the last match, and the part after it. 518 | *

519 | * If no such match is found in this CharSeq, return a Seq 520 | * of CharSeq consists of two empty CharSeqs and the CharSeq itself. 521 | * 522 | * @param regex Regular Expression 523 | * @return A Seq of CharSeq 524 | */ 525 | public Seq rPartition(String regex) { 526 | Matcher m = Pattern.compile(regex).matcher(str); 527 | 528 | String match = null; 529 | int start = 0, end = 0; 530 | while (m.find()) { 531 | match = m.group(); 532 | start = m.start(); 533 | end = m.end(); 534 | } 535 | if (match != null) { 536 | return Seqs.newSeq(CharSeq.of(str.substring(0, start)), 537 | CharSeq.of(match), 538 | CharSeq.of(str.substring(end)) 539 | ); 540 | } 541 | return Seqs.newSeq(CharSeq.of(""), CharSeq.of(""), CharSeq.of(str)); 542 | } 543 | 544 | /** 545 | * Converts this CharSeq to a new Character Seq. 546 | * 547 | * @return A Seq of Character 548 | */ 549 | public Seq eachChar() { 550 | char[] chars = str.toCharArray(); 551 | Character[] characters = new Character[str.length()]; 552 | for (int i = 0; i < characters.length; i++) { 553 | characters[i] = chars[i]; 554 | } 555 | return Seqs.newSeq(characters); 556 | } 557 | 558 | /** 559 | * Encodes this {@code CharSeq} into a sequence of bytes using the 560 | * platform's default charset, storing the result into a new Byte Seq. 561 | * 562 | * @return A Seq of Byte 563 | */ 564 | public Seq eachByte() { 565 | byte[] rawBytes = str.getBytes(); 566 | Byte[] bytes = new Byte[rawBytes.length]; 567 | for (int i = 0; i < bytes.length; i++) { 568 | bytes[i] = rawBytes[i]; 569 | } 570 | return Seqs.newSeq(bytes); 571 | } 572 | 573 | /** 574 | * Returns the collection of the Unicode of each character in this {@code CharSeq}. 575 | * 576 | * @return the collection of ths Unicode of each character 577 | */ 578 | public Seq eachCodePoint() { 579 | MutableSeq codePoints = Seqs.newMutableSeq(); 580 | char[] chars = str.toCharArray(); 581 | for (int i = 0; i < chars.length; i++) { 582 | codePoints.appendInPlace((int) chars[i]); 583 | } 584 | return codePoints; 585 | } 586 | 587 | /** 588 | * Takes action on the Unicode of each character in this {@code CharSeq}. 589 | *

590 | * Similar to {@link #eachCodePoint()}, but instead of returning the Unicode collection, it iterates the collection and takes action. 591 | *

592 | * 593 | * @param consumer the action to be taken on the Unicode of each character 594 | * @throws NullPointerException if consumer is null 595 | */ 596 | public CharSeq forEachCodePoint(Consumer consumer) { 597 | Objects.requireNonNull(consumer); 598 | eachCodePoint().forEach(consumer); 599 | return this; 600 | } 601 | 602 | /** 603 | * Check whether this CharSeq contains the sub seq, if the given seq is empty, always return true. 604 | * 605 | * @throws NullPointerException if seq is null 606 | */ 607 | public boolean containsSubSeq(String seq) { 608 | return indexOfSubSeq(seq) != -1; 609 | } 610 | 611 | /** 612 | * Return the first index of the given sub seq, or -1 if the given seq is not a sub seq. 613 | * If the given seq is empty, always return 0. 614 | * 615 | * @throws NullPointerException if seq is null 616 | */ 617 | public int indexOfSubSeq(String seq) { 618 | return indexOfSubSeq(CharSeq.of(seq)); 619 | } 620 | 621 | /** 622 | * Return the last index of the given sub seq, or -1 if the given seq is not a sub seq. 623 | * If the given seq is empty, always return 0. 624 | * 625 | * @throws NullPointerException if seq is null 626 | */ 627 | public int lastIndexOfSubSeq(String seq) { 628 | return lastIndexOfSubSeq(CharSeq.of(seq)); 629 | } 630 | 631 | /** 632 | * Check whether this CharSeq contains the sub seq, if the given seq is empty, always return true. 633 | * 634 | * @throws NullPointerException if seq is null 635 | */ 636 | public boolean containsSubSeq(CharSeq seq) { 637 | return indexOfSubSeq(seq) != -1; 638 | } 639 | 640 | /** 641 | * Return the first index of the given sub seq, or -1 if the given seq is not a sub seq. 642 | * If the given seq is empty, always return 0. 643 | * 644 | * @throws NullPointerException if seq is null 645 | */ 646 | public int indexOfSubSeq(CharSeq seq) { 647 | Objects.requireNonNull(seq); 648 | 649 | if (seq.isEmpty()) { 650 | return 0; 651 | } 652 | 653 | if (length() < seq.length()) { 654 | return -1; 655 | } 656 | 657 | //Sunday algorithm 658 | 659 | Map lastIndex = new HashMap<>(); 660 | seq.forEachChar(lastIndex::put); 661 | 662 | int startI = 0, size = length(), len = seq.length(); 663 | while (size - startI >= len) { 664 | for (int i = 0; ; i++) { 665 | if (!charAt(startI + i).equals(seq.charAt(i))) { 666 | if (startI + len >= size) { 667 | return -1; 668 | } 669 | Character next = charAt(startI + len); 670 | Integer last = lastIndex.get(next); 671 | if (last == null) { 672 | startI += len + 1; 673 | } else { 674 | startI += len - last; 675 | } 676 | break; 677 | } else if (i == len - 1) { 678 | return startI; 679 | } 680 | } 681 | } 682 | return -1; 683 | } 684 | 685 | /** 686 | * Return the last index of the given sub seq, or -1 if the given seq is not a sub seq. 687 | * If the given seq is empty, always return 0. 688 | * 689 | * @throws NullPointerException if seq is null 690 | */ 691 | public int lastIndexOfSubSeq(CharSeq seq) { 692 | Objects.requireNonNull(seq); 693 | 694 | if (seq.isEmpty()) { 695 | return 0; 696 | } 697 | 698 | if (length() < seq.length()) { 699 | return -1; 700 | } 701 | 702 | //Sunday algorithm 703 | 704 | Map lastIndex = new HashMap<>(); 705 | for (int i = seq.length() - 1; i >= 0; i--) { 706 | lastIndex.put(seq.charAt(i), i); 707 | } 708 | 709 | int size = length(), endI = size - 1, len = seq.length(); 710 | while (endI >= len - 1) { 711 | for (int i = 0; ; i++) { 712 | if (!charAt(endI - i).equals(seq.charAt(len - 1 - i))) { 713 | if (endI - len < 0) { 714 | return -1; 715 | } 716 | Character next = charAt(endI - len); 717 | Integer last = lastIndex.get(next); 718 | if (last == null) { 719 | endI -= len + 1; 720 | } else { 721 | endI -= last + 1; 722 | } 723 | break; 724 | } else if (i == len - 1) { 725 | return endI - len + 1; 726 | } 727 | } 728 | } 729 | return -1; 730 | } 731 | } 732 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | 204 | 205 | APACHE HTTP SERVER SUBCOMPONENTS: 206 | 207 | The Apache HTTP Server includes a number of subcomponents with 208 | separate copyright notices and license terms. Your use of the source 209 | code for the these subcomponents is subject to the terms and 210 | conditions of the following licenses. 211 | 212 | For the mod_mime_magic component: 213 | 214 | /* 215 | * mod_mime_magic: MIME type lookup via file magic numbers 216 | * Copyright (c) 1996-1997 Cisco Systems, Inc. 217 | * 218 | * This software was submitted by Cisco Systems to the Apache Group in July 219 | * 1997. Future revisions and derivatives of this source code must 220 | * acknowledge Cisco Systems as the original contributor of this module. 221 | * All other licensing and usage conditions are those of the Apache Group. 222 | * 223 | * Some of this code is derived from the free version of the file command 224 | * originally posted to comp.sources.unix. Copyright info for that program 225 | * is included below as required. 226 | * --------------------------------------------------------------------------- 227 | * - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin. 228 | * 229 | * This software is not subject to any license of the American Telephone and 230 | * Telegraph Company or of the Regents of the University of California. 231 | * 232 | * Permission is granted to anyone to use this software for any purpose on any 233 | * computer system, and to alter it and redistribute it freely, subject to 234 | * the following restrictions: 235 | * 236 | * 1. The author is not responsible for the consequences of use of this 237 | * software, no matter how awful, even if they arise from flaws in it. 238 | * 239 | * 2. The origin of this software must not be misrepresented, either by 240 | * explicit claim or by omission. Since few users ever read sources, credits 241 | * must appear in the documentation. 242 | * 243 | * 3. Altered versions must be plainly marked as such, and must not be 244 | * misrepresented as being the original software. Since few users ever read 245 | * sources, credits must appear in the documentation. 246 | * 247 | * 4. This notice may not be removed or altered. 248 | * ------------------------------------------------------------------------- 249 | * 250 | */ 251 | 252 | 253 | For the modules\mappers\mod_imap.c component: 254 | 255 | "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com 256 | 257 | For the server\util_md5.c component: 258 | 259 | /************************************************************************ 260 | * NCSA HTTPd Server 261 | * Software Development Group 262 | * National Center for Supercomputing Applications 263 | * University of Illinois at Urbana-Champaign 264 | * 605 E. Springfield, Champaign, IL 61820 265 | * httpd@ncsa.uiuc.edu 266 | * 267 | * Copyright (C) 1995, Board of Trustees of the University of Illinois 268 | * 269 | ************************************************************************ 270 | * 271 | * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code 272 | * 273 | * Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc. 274 | * Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon 275 | * University (see Copyright below). 276 | * Portions of Content-MD5 code Copyright (C) 1991 Bell Communications 277 | * Research, Inc. (Bellcore) (see Copyright below). 278 | * Portions extracted from mpack, John G. Myers - jgm+@cmu.edu 279 | * Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk) 280 | * 281 | */ 282 | 283 | 284 | /* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */ 285 | /* (C) Copyright 1993,1994 by Carnegie Mellon University 286 | * All Rights Reserved. 287 | * 288 | * Permission to use, copy, modify, distribute, and sell this software 289 | * and its documentation for any purpose is hereby granted without 290 | * fee, provided that the above copyright notice appear in all copies 291 | * and that both that copyright notice and this permission notice 292 | * appear in supporting documentation, and that the name of Carnegie 293 | * Mellon University not be used in advertising or publicity 294 | * pertaining to distribution of the software without specific, 295 | * written prior permission. Carnegie Mellon University makes no 296 | * representations about the suitability of this software for any 297 | * purpose. It is provided "as is" without express or implied 298 | * warranty. 299 | * 300 | * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 301 | * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 302 | * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 303 | * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 304 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 305 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 306 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 307 | * SOFTWARE. 308 | */ 309 | 310 | /* 311 | * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) 312 | * 313 | * Permission to use, copy, modify, and distribute this material 314 | * for any purpose and without fee is hereby granted, provided 315 | * that the above copyright notice and this permission notice 316 | * appear in all copies, and that the name of Bellcore not be 317 | * used in advertising or publicity pertaining to this 318 | * material without the specific, prior written permission 319 | * of an authorized representative of Bellcore. BELLCORE 320 | * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 321 | * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", 322 | * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. 323 | */ 324 | 325 | For the srclib\apr\include\apr_md5.h component: 326 | /* 327 | * This is work is derived from material Copyright RSA Data Security, Inc. 328 | * 329 | * The RSA copyright statement and Licence for that original material is 330 | * included below. This is followed by the Apache copyright statement and 331 | * licence for the modifications made to that material. 332 | */ 333 | 334 | /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 335 | rights reserved. 336 | 337 | License to copy and use this software is granted provided that it 338 | is identified as the "RSA Data Security, Inc. MD5 Message-Digest 339 | Algorithm" in all material mentioning or referencing this software 340 | or this function. 341 | 342 | License is also granted to make and use derivative works provided 343 | that such works are identified as "derived from the RSA Data 344 | Security, Inc. MD5 Message-Digest Algorithm" in all material 345 | mentioning or referencing the derived work. 346 | 347 | RSA Data Security, Inc. makes no representations concerning either 348 | the merchantability of this software or the suitability of this 349 | software for any particular purpose. It is provided "as is" 350 | without express or implied warranty of any kind. 351 | 352 | These notices must be retained in any copies of any part of this 353 | documentation and/or software. 354 | */ 355 | 356 | For the srclib\apr\passwd\apr_md5.c component: 357 | 358 | /* 359 | * This is work is derived from material Copyright RSA Data Security, Inc. 360 | * 361 | * The RSA copyright statement and Licence for that original material is 362 | * included below. This is followed by the Apache copyright statement and 363 | * licence for the modifications made to that material. 364 | */ 365 | 366 | /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm 367 | */ 368 | 369 | /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 370 | rights reserved. 371 | 372 | License to copy and use this software is granted provided that it 373 | is identified as the "RSA Data Security, Inc. MD5 Message-Digest 374 | Algorithm" in all material mentioning or referencing this software 375 | or this function. 376 | 377 | License is also granted to make and use derivative works provided 378 | that such works are identified as "derived from the RSA Data 379 | Security, Inc. MD5 Message-Digest Algorithm" in all material 380 | mentioning or referencing the derived work. 381 | 382 | RSA Data Security, Inc. makes no representations concerning either 383 | the merchantability of this software or the suitability of this 384 | software for any particular purpose. It is provided "as is" 385 | without express or implied warranty of any kind. 386 | 387 | These notices must be retained in any copies of any part of this 388 | documentation and/or software. 389 | */ 390 | /* 391 | * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 392 | * MD5 crypt() function, which is licenced as follows: 393 | * ---------------------------------------------------------------------------- 394 | * "THE BEER-WARE LICENSE" (Revision 42): 395 | * wrote this file. As long as you retain this notice you 396 | * can do whatever you want with this stuff. If we meet some day, and you think 397 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 398 | * ---------------------------------------------------------------------------- 399 | */ 400 | 401 | For the srclib\apr-util\crypto\apr_md4.c component: 402 | 403 | * This is derived from material copyright RSA Data Security, Inc. 404 | * Their notice is reproduced below in its entirety. 405 | * 406 | * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 407 | * rights reserved. 408 | * 409 | * License to copy and use this software is granted provided that it 410 | * is identified as the "RSA Data Security, Inc. MD4 Message-Digest 411 | * Algorithm" in all material mentioning or referencing this software 412 | * or this function. 413 | * 414 | * License is also granted to make and use derivative works provided 415 | * that such works are identified as "derived from the RSA Data 416 | * Security, Inc. MD4 Message-Digest Algorithm" in all material 417 | * mentioning or referencing the derived work. 418 | * 419 | * RSA Data Security, Inc. makes no representations concerning either 420 | * the merchantability of this software or the suitability of this 421 | * software for any particular purpose. It is provided "as is" 422 | * without express or implied warranty of any kind. 423 | * 424 | * These notices must be retained in any copies of any part of this 425 | * documentation and/or software. 426 | */ 427 | 428 | For the srclib\apr-util\include\apr_md4.h component: 429 | 430 | * 431 | * This is derived from material copyright RSA Data Security, Inc. 432 | * Their notice is reproduced below in its entirety. 433 | * 434 | * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 435 | * rights reserved. 436 | * 437 | * License to copy and use this software is granted provided that it 438 | * is identified as the "RSA Data Security, Inc. MD4 Message-Digest 439 | * Algorithm" in all material mentioning or referencing this software 440 | * or this function. 441 | * 442 | * License is also granted to make and use derivative works provided 443 | * that such works are identified as "derived from the RSA Data 444 | * Security, Inc. MD4 Message-Digest Algorithm" in all material 445 | * mentioning or referencing the derived work. 446 | * 447 | * RSA Data Security, Inc. makes no representations concerning either 448 | * the merchantability of this software or the suitability of this 449 | * software for any particular purpose. It is provided "as is" 450 | * without express or implied warranty of any kind. 451 | * 452 | * These notices must be retained in any copies of any part of this 453 | * documentation and/or software. 454 | */ 455 | 456 | 457 | For the srclib\apr-util\test\testdbm.c component: 458 | 459 | /* ==================================================================== 460 | * The Apache Software License, Version 1.1 461 | * 462 | * Copyright (c) 2000-2002 The Apache Software Foundation. All rights 463 | * reserved. 464 | * 465 | * Redistribution and use in source and binary forms, with or without 466 | * modification, are permitted provided that the following conditions 467 | * are met: 468 | * 469 | * 1. Redistributions of source code must retain the above copyright 470 | * notice, this list of conditions and the following disclaimer. 471 | * 472 | * 2. Redistributions in binary form must reproduce the above copyright 473 | * notice, this list of conditions and the following disclaimer in 474 | * the documentation and/or other materials provided with the 475 | * distribution. 476 | * 477 | * 3. The end-user documentation included with the redistribution, 478 | * if any, must include the following acknowledgment: 479 | * "This product includes software developed by the 480 | * Apache Software Foundation (http://www.apache.org/)." 481 | * Alternately, this acknowledgment may appear in the software itself, 482 | * if and wherever such third-party acknowledgments normally appear. 483 | * 484 | * 4. The names "Apache" and "Apache Software Foundation" must 485 | * not be used to endorse or promote products derived from this 486 | * software without prior written permission. For written 487 | * permission, please contact apache@apache.org. 488 | * 489 | * 5. Products derived from this software may not be called "Apache", 490 | * nor may "Apache" appear in their name, without prior written 491 | * permission of the Apache Software Foundation. 492 | * 493 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 494 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 495 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 496 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 497 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 498 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 499 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 500 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 501 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 502 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 503 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 504 | * SUCH DAMAGE. 505 | * ==================================================================== 506 | * 507 | * This software consists of voluntary contributions made by many 508 | * individuals on behalf of the Apache Software Foundation. For more 509 | * information on the Apache Software Foundation, please see 510 | * . 511 | * 512 | * This file came from the SDBM package (written by oz@nexus.yorku.ca). 513 | * That package was under public domain. This file has been ported to 514 | * APR, updated to ANSI C and other, newer idioms, and added to the Apache 515 | * codebase under the above copyright and license. 516 | */ 517 | 518 | 519 | For the srclib\apr-util\test\testmd4.c component: 520 | 521 | * 522 | * This is derived from material copyright RSA Data Security, Inc. 523 | * Their notice is reproduced below in its entirety. 524 | * 525 | * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All 526 | * rights reserved. 527 | * 528 | * RSA Data Security, Inc. makes no representations concerning either 529 | * the merchantability of this software or the suitability of this 530 | * software for any particular purpose. It is provided "as is" 531 | * without express or implied warranty of any kind. 532 | * 533 | * These notices must be retained in any copies of any part of this 534 | * documentation and/or software. 535 | */ 536 | 537 | For the srclib\apr-util\xml\expat\conftools\install-sh component: 538 | 539 | # 540 | # install - install a program, script, or datafile 541 | # This comes from X11R5 (mit/util/scripts/install.sh). 542 | # 543 | # Copyright 1991 by the Massachusetts Institute of Technology 544 | # 545 | # Permission to use, copy, modify, distribute, and sell this software and its 546 | # documentation for any purpose is hereby granted without fee, provided that 547 | # the above copyright notice appear in all copies and that both that 548 | # copyright notice and this permission notice appear in supporting 549 | # documentation, and that the name of M.I.T. not be used in advertising or 550 | # publicity pertaining to distribution of the software without specific, 551 | # written prior permission. M.I.T. makes no representations about the 552 | # suitability of this software for any purpose. It is provided "as is" 553 | # without express or implied warranty. 554 | # 555 | 556 | For the srclib\pcre\install-sh component: 557 | 558 | # 559 | # Copyright 1991 by the Massachusetts Institute of Technology 560 | # 561 | # Permission to use, copy, modify, distribute, and sell this software and its 562 | # documentation for any purpose is hereby granted without fee, provided that 563 | # the above copyright notice appear in all copies and that both that 564 | # copyright notice and this permission notice appear in supporting 565 | # documentation, and that the name of M.I.T. not be used in advertising or 566 | # publicity pertaining to distribution of the software without specific, 567 | # written prior permission. M.I.T. makes no representations about the 568 | # suitability of this software for any purpose. It is provided "as is" 569 | # without express or implied warranty. 570 | 571 | For the pcre component: 572 | 573 | PCRE LICENCE 574 | ------------ 575 | 576 | PCRE is a library of functions to support regular expressions whose syntax 577 | and semantics are as close as possible to those of the Perl 5 language. 578 | 579 | Written by: Philip Hazel 580 | 581 | University of Cambridge Computing Service, 582 | Cambridge, England. Phone: +44 1223 334714. 583 | 584 | Copyright (c) 1997-2001 University of Cambridge 585 | 586 | Permission is granted to anyone to use this software for any purpose on any 587 | computer system, and to redistribute it freely, subject to the following 588 | restrictions: 589 | 590 | 1. This software is distributed in the hope that it will be useful, 591 | but WITHOUT ANY WARRANTY; without even the implied warranty of 592 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 593 | 594 | 2. The origin of this software must not be misrepresented, either by 595 | explicit claim or by omission. In practice, this means that if you use 596 | PCRE in software which you distribute to others, commercially or 597 | otherwise, you must put a sentence like this 598 | 599 | Regular expression support is provided by the PCRE library package, 600 | which is open source software, written by Philip Hazel, and copyright 601 | by the University of Cambridge, England. 602 | 603 | somewhere reasonably visible in your documentation and in any relevant 604 | files or online help data or similar. A reference to the ftp site for 605 | the source, that is, to 606 | 607 | ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ 608 | 609 | should also be given in the documentation. 610 | 611 | 3. Altered versions must be plainly marked as such, and must not be 612 | misrepresented as being the original software. 613 | 614 | 4. If PCRE is embedded in any software that is released under the GNU 615 | General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL), 616 | then the terms of that licence shall supersede any condition above with 617 | which it is incompatible. 618 | 619 | The documentation for PCRE, supplied in the "doc" directory, is distributed 620 | under the same terms as the software itself. 621 | 622 | End PCRE LICENCE 623 | 624 | 625 | For the test\zb.c component: 626 | 627 | /* ZeusBench V1.01 628 | =============== 629 | 630 | This program is Copyright (C) Zeus Technology Limited 1996. 631 | 632 | This program may be used and copied freely providing this copyright notice 633 | is not removed. 634 | 635 | This software is provided "as is" and any express or implied waranties, 636 | including but not limited to, the implied warranties of merchantability and 637 | fitness for a particular purpose are disclaimed. In no event shall 638 | Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, 639 | exemplary, or consequential damaged (including, but not limited to, 640 | procurement of substitute good or services; loss of use, data, or profits; 641 | or business interruption) however caused and on theory of liability. Whether 642 | in contract, strict liability or tort (including negligence or otherwise) 643 | arising in any way out of the use of this software, even if advised of the 644 | possibility of such damage. 645 | 646 | Written by Adam Twiss (adam@zeus.co.uk). March 1996 647 | 648 | Thanks to the following people for their input: 649 | Mike Belshe (mbelshe@netscape.com) 650 | Michael Campanella (campanella@stevms.enet.dec.com) 651 | 652 | */ 653 | 654 | For the expat xml parser component: 655 | 656 | Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd 657 | and Clark Cooper 658 | 659 | Permission is hereby granted, free of charge, to any person obtaining 660 | a copy of this software and associated documentation files (the 661 | "Software"), to deal in the Software without restriction, including 662 | without limitation the rights to use, copy, modify, merge, publish, 663 | distribute, sublicense, and/or sell copies of the Software, and to 664 | permit persons to whom the Software is furnished to do so, subject to 665 | the following conditions: 666 | 667 | The above copyright notice and this permission notice shall be included 668 | in all copies or substantial portions of the Software. 669 | 670 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 671 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 672 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 673 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 674 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 675 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 676 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 677 | 678 | ==================================================================== 679 | -------------------------------------------------------------------------------- /src/main/java/com/worksap/icefig/lang/Seq.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Fig Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.worksap.icefig.lang; 18 | 19 | import java.util.*; 20 | import java.util.function.*; 21 | 22 | /** 23 | * Elegant supplement for List in JDK 24 | */ 25 | public interface Seq { 26 | /** 27 | * Transform each element of the seq into another value using the same function, resulting a new seq without changing the original one. 28 | * 29 | * @return The new seq after transformation. 30 | * @throws NullPointerException if func is null 31 | */ 32 | Seq map(Function func); 33 | 34 | /** 35 | * Similar to {@link #map(Function)}, with additional parameter "index" as the second parameter of the lambda expression. 36 | * 37 | * @throws NullPointerException if func is null 38 | */ 39 | Seq map(BiFunction func); 40 | 41 | /** 42 | * Transform each element into a seq, and concat all seq together into a new seq. 43 | * 44 | * @return The new seq after transformation. 45 | * @throws NullPointerException if func is null 46 | */ 47 | Seq flatMap(Function> func); 48 | 49 | /** 50 | * Similar to {@link #flatMap(Function)}, with additional parameter "index" as the second parameter of the lambda expression. 51 | * 52 | * @throws NullPointerException if func is null 53 | */ 54 | Seq flatMap(BiFunction> func); 55 | 56 | /** 57 | * @return The beginning element of the seq. 58 | * @throws IndexOutOfBoundsException if the seq is empty 59 | */ 60 | default T first() { 61 | return get(0); 62 | } 63 | 64 | /** 65 | * @return The ending element of the seq. 66 | * @throws IndexOutOfBoundsException if the seq is empty 67 | */ 68 | default T last() { 69 | return get(size() - 1); 70 | } 71 | 72 | /** 73 | * Convert all elements into String, and connect each String together to a single String, following the same order of the seq. 74 | */ 75 | default CharSeq join() { 76 | return join(""); 77 | } 78 | 79 | /** 80 | * Convert all elements into String, and connect each String together to a single String, following the same order of the seq. 81 | * Insert a delimiter at each connection point. 82 | */ 83 | default CharSeq join(CharSequence delimiter) { 84 | return join(delimiter, "", ""); 85 | } 86 | 87 | /** 88 | * Convert all elements into String, and connect each String together to a single String, following the same order of the seq. 89 | * Insert a delimiter at each connection point. Add prefix and suffix to the final result. 90 | */ 91 | default CharSeq join(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { 92 | StringBuilder stringBuilder = new StringBuilder(prefix); 93 | forEach((t, i) -> { 94 | if (i != 0) { 95 | stringBuilder.append(delimiter); 96 | } 97 | stringBuilder.append(t); 98 | }); 99 | stringBuilder.append(suffix); 100 | return CharSeq.of(stringBuilder.toString()); 101 | } 102 | 103 | /** 104 | * Randomly find an element in the seq. 105 | * 106 | * @return The selected element, or null if the seq is empty. 107 | */ 108 | default T sample() { 109 | if (size() == 0) { 110 | return null; 111 | } 112 | Random rand = new Random(); 113 | int randomNum = rand.nextInt(size()); 114 | return get(randomNum); 115 | } 116 | 117 | /** 118 | * Randomly find n elements in the seq. 119 | * 120 | * @return A new seq of the selected elements. If the size of seq is lower than n, return all elements. 121 | * Return empty result if the seq is empty. The order of selected elements may be changed. 122 | */ 123 | Seq sample(int n); 124 | 125 | /** 126 | * Get the number of elements in this seq. 127 | */ 128 | int size(); 129 | 130 | boolean contains(T t); 131 | 132 | /** 133 | * Randomly shuffle the seq, resulting a new seq. 134 | */ 135 | Seq shuffle(); 136 | 137 | ArrayList toArrayList(); 138 | 139 | 140 | /** 141 | * Iterate each element of the seq. 142 | * 143 | * @throws NullPointerException if action is null 144 | */ 145 | default void forEach(Consumer action) { 146 | Objects.requireNonNull(action); 147 | for (int i = 0; i < this.size(); i++) { 148 | action.accept(this.get(i)); 149 | } 150 | } 151 | 152 | /** 153 | * Similar to {@link #forEach(Consumer)}, with additional parameter "index" as the second parameter of the lambda expression. 154 | * 155 | * @throws NullPointerException if action is null 156 | */ 157 | default void forEach(BiConsumer action) { 158 | Objects.requireNonNull(action); 159 | for (int i = 0; i < this.size(); i++) { 160 | action.accept(this.get(i), i); 161 | } 162 | } 163 | 164 | /** 165 | * Iterate each element of the seq from the end to the beginning. 166 | * 167 | * @throws NullPointerException if action is null 168 | */ 169 | default void forEachReverse(Consumer action) { 170 | Objects.requireNonNull(action); 171 | for (int i = size() - 1; i >= 0; i--) { 172 | action.accept(this.get(i)); 173 | } 174 | } 175 | 176 | /** 177 | * Similar to {@link #forEachReverse(Consumer)}, with additional parameter "index" as the second parameter of the lambda expression. 178 | * 179 | * @throws NullPointerException if action is null 180 | */ 181 | default void forEachReverse(BiConsumer action) { 182 | Objects.requireNonNull(action); 183 | for (int i = size() - 1; i >= 0; i--) { 184 | action.accept(this.get(i), i); 185 | } 186 | } 187 | 188 | 189 | /** 190 | * Transform the seq, to a seq of each continuous n elements starting from each element. 191 | *

192 | * [1, 2, 3, 4] with n=2 will result to [[1, 2], [2, 3], [3, 4]] 193 | *

194 | * If the size of seq is lower than n, result will be empty. 195 | * 196 | * @throws IllegalArgumentException if n <= 0 197 | */ 198 | Seq> eachCons(int n); 199 | 200 | /** 201 | * Similar with {@link #eachCons(int)}, but instead of to return the transformed seq, it iterates the transformed seq and do action. 202 | * 203 | * @throws NullPointerException if action is null 204 | * @throws IllegalArgumentException if n <= 0 205 | */ 206 | default void forEachCons(int n, Consumer> action) { 207 | Objects.requireNonNull(action); 208 | if (n <= 0) { 209 | throw new IllegalArgumentException("n should be a positive number!"); 210 | } 211 | for (int i = 0; i <= this.size() - n; i++) { 212 | action.accept(this.subSeq(i, i + n)); 213 | } 214 | } 215 | 216 | boolean isEmpty(); 217 | 218 | Object[] toArray(); 219 | 220 | /** 221 | * Sort the seq by the comparator, resulting a new seq, without changing the original seq. 222 | * 223 | * @param comparator the comparator to determine the order of the seq. A 224 | * {@code null} value indicates that the elements' natural 225 | * ordering should be used. 226 | * @return A new seq sorted 227 | */ 228 | Seq sort(Comparator comparator); 229 | 230 | /** 231 | * Reduce duplicated elements, keeping only the first occurrence, resulting a new seq. 232 | * 233 | * @return A new seq reduced 234 | */ 235 | Seq distinct(); 236 | 237 | /** 238 | * Find the first element which satisfy the condition. 239 | * 240 | * @return The element, or null if no element found. 241 | * @throws NullPointerException if condition is null 242 | */ 243 | default T findFirst(Predicate condition) { 244 | Objects.requireNonNull(condition); 245 | for (int i = 0; i < size(); i++) { 246 | T t = get(i); 247 | if (condition.test(t)) { 248 | return t; 249 | } 250 | } 251 | return null; 252 | } 253 | 254 | /** 255 | * Find the last element which satisfy the condition. 256 | * 257 | * @return The element, or null if no element found. 258 | * @throws NullPointerException if condition is null 259 | */ 260 | default T findLast(Predicate condition) { 261 | Objects.requireNonNull(condition); 262 | for (int i = size() - 1; i >= 0; i--) { 263 | T t = get(i); 264 | if (condition.test(t)) { 265 | return t; 266 | } 267 | } 268 | return null; 269 | } 270 | 271 | /** 272 | * Find the index of the first element which satisfy the condition. 273 | * 274 | * @return The index, or -1 if no element found. 275 | * @throws NullPointerException if condition is null 276 | */ 277 | default int findFirstIndex(Predicate condition) { 278 | Objects.requireNonNull(condition); 279 | for (int i = 0; i < size(); i++) { 280 | T t = get(i); 281 | if (condition.test(t)) { 282 | return i; 283 | } 284 | } 285 | return -1; 286 | } 287 | 288 | /** 289 | * Find the index of the last element which satisfy the condition. 290 | * 291 | * @return The index, or -1 if no element found. 292 | * @throws NullPointerException if condition is null 293 | */ 294 | default int findLastIndex(Predicate condition) { 295 | Objects.requireNonNull(condition); 296 | for (int i = size() - 1; i >= 0; i--) { 297 | T t = get(i); 298 | if (condition.test(t)) { 299 | return i; 300 | } 301 | } 302 | return -1; 303 | } 304 | 305 | /** 306 | * Append values to the seq at the end of it, resulting a new seq. 307 | *

308 | * Note: This method does NOT change the seq. 309 | *

310 | * 311 | * @return A new seq after the change 312 | * @throws NullPointerException is values is null 313 | */ 314 | Seq append(T value); 315 | 316 | /** 317 | * Append values to the seq at the end of it, resulting a new seq. 318 | *

319 | * Note: This method does NOT change the seq. 320 | *

321 | * 322 | * @return A new seq after the change 323 | * @throws NullPointerException is values is null 324 | */ 325 | @SuppressWarnings({"unchecked", "varargs"}) 326 | Seq append(T... values); 327 | 328 | /** 329 | * Append values to the seq at the end of it, resulting a new seq. 330 | *

331 | * Note: This method does NOT change the seq. 332 | *

333 | * 334 | * @return A new seq after the change 335 | * @throws NullPointerException is collection is null 336 | */ 337 | Seq append(Collection collection); 338 | 339 | /** 340 | * Append values to the seq at the end of it, resulting a new seq. 341 | *

342 | * Note: This method does NOT change the seq. 343 | *

344 | * 345 | * @return A new seq after the change 346 | * @throws NullPointerException is collection is null 347 | */ 348 | Seq append(Seq seq); 349 | 350 | /** 351 | * Prepend values into the seq at the beginning of it. Values keep the parameter order after being prepended. 352 | *

353 | * Note: This method does NOT change the seq. 354 | *

355 | * 356 | * @return A new seq after the change 357 | * @throws NullPointerException is values is null 358 | */ 359 | Seq prepend(T values); 360 | 361 | /** 362 | * Prepend values into the seq at the beginning of it. Values keep the parameter order after being prepended. 363 | *

364 | * Note: This method does NOT change the seq. 365 | *

366 | * 367 | * @return A new seq after the change 368 | * @throws NullPointerException is values is null 369 | */ 370 | @SuppressWarnings({"unchecked", "varargs"}) 371 | Seq prepend(T... values); 372 | 373 | /** 374 | * Prepend values into the seq at the beginning of it. Values keep the order after being merged into the seq. 375 | *

376 | * Note: This method does NOT change the seq. 377 | *

378 | * 379 | * @return A new seq after the change 380 | * @throws NullPointerException is collection is null 381 | */ 382 | Seq prepend(Collection collection); 383 | 384 | /** 385 | * Prepend values into the seq at the beginning of it. Values keep the order after being merged into the seq. 386 | *

387 | * Note: This method does NOT change the seq. 388 | *

389 | * 390 | * @return A new seq after the change 391 | * @throws NullPointerException is collection is null 392 | */ 393 | Seq prepend(Seq seq); 394 | 395 | /** 396 | * Create a new seq which is the sub seq of the current one. 397 | * 398 | * @param fromIndex The start index, inclusive. 399 | * @param toIndex The end index, exclusive. 400 | * @throws IndexOutOfBoundsException if an endpoint index value is out of range 401 | * {@code (fromIndex < 0 || toIndex > size)} 402 | * @throws IllegalArgumentException if the endpoint indices are out of order 403 | * {@code (fromIndex > toIndex)} 404 | */ 405 | Seq subSeq(int fromIndex, int toIndex); 406 | 407 | /** 408 | * Removes elements which satisfy the condition, resulting a new seq without changing the original one. 409 | * 410 | * @param condition the condition used to filter elements by passing the element, 411 | * returns true if the element satisfies the condition, otherwise returns false. 412 | * @return the new seq without elements which satisfy the condition 413 | * @throws NullPointerException if condition is null 414 | */ 415 | Seq reject(Predicate condition); 416 | 417 | /** 418 | * Removes elements which satisfy the condition, resulting a new seq without changing the original one. 419 | *

420 | * Similar to {@link #reject(Predicate)}, with additional parameter "index" as the second parameter of the lambda expression 421 | *

422 | * 423 | * @param condition the condition used to filter elements by passing the element and its index, 424 | * returns true if the element satisfies the condition, otherwise returns false. 425 | * @return the new seq without elements which satisfy the condition 426 | * @throws NullPointerException if condition is null 427 | */ 428 | Seq reject(BiPredicate condition); 429 | 430 | /** 431 | * Removes elements at the front of this seq which satisfy the condition, resulting a new seq without changing the original one. 432 | * 433 | * @param condition the condition used to filter elements by passing the element, 434 | * returns true if the element satisfies the condition, otherwise returns false. 435 | * @return the new seq without elements which satisfy the condition 436 | * @throws NullPointerException if condition is null 437 | */ 438 | Seq rejectWhile(Predicate condition); 439 | 440 | /** 441 | * Removes elements at the front of this seq which satisfy the condition, resulting a new seq without changing the original one. 442 | *

443 | * Similar to {@link #reject(Predicate)}, with additional parameter "index" as the second parameter of the lambda expression 444 | *

445 | * 446 | * @param condition the condition used to filter elements by passing the element and its index, 447 | * returns true if the element satisfies the condition, otherwise returns false. 448 | * @return the new seq without elements which satisfy the condition 449 | * @throws NullPointerException if condition is null 450 | */ 451 | Seq rejectWhile(BiPredicate condition); 452 | 453 | /** 454 | * Gets elements which satisfy the condition, resulting a new seq without changing the original one. 455 | * 456 | * @param condition the condition used to filter elements by passing the element's index, 457 | * returns true if the element satisfies the condition, otherwise returns false 458 | * @return the new seq containing only the elements satisfying the condition 459 | * @throws NullPointerException if condition is null 460 | */ 461 | Seq filter(Predicate condition); 462 | 463 | /** 464 | * Gets elements which satisfy the condition, resulting a new seq without changing the original one. 465 | *

466 | * Similar to {@link #filter(Predicate)}, with additional parameter "index" as the second parameter of the lambda expression. 467 | *

468 | * 469 | * @param condition the condition used to filter elements by passing the element and its index, 470 | * returns true if the element satisfies the condition, otherwise returns false 471 | * @return the new seq containing only the elements satisfying the condition 472 | * @throws NullPointerException if condition is null 473 | */ 474 | Seq filter(BiPredicate condition); 475 | 476 | /** 477 | * Gets elements at the front of this seq which satisfy the condition, resulting a new seq without changing the original one. 478 | * 479 | * @param condition the condition used to filter elements by passing the element's index, 480 | * returns true if the element satisfies the condition, otherwise returns false 481 | * @return the new seq containing only the elements satisfying the condition 482 | * @throws NullPointerException if condition is null 483 | */ 484 | Seq filterWhile(Predicate condition); 485 | 486 | /** 487 | * Gets elements at the front of this seq which satisfy the condition, resulting a new seq without changing the original one. 488 | *

489 | * Similar to {@link #filter(Predicate)}, with additional parameter "index" as the second parameter of the lambda expression. 490 | *

491 | * 492 | * @param condition the condition used to filter elements by passing the element and its index, 493 | * returns true if the element satisfies the condition, otherwise returns false 494 | * @return the new seq containing only the elements satisfying the condition 495 | * @throws NullPointerException if condition is null 496 | */ 497 | Seq filterWhile(BiPredicate condition); 498 | 499 | /** 500 | * Returns a new seq built by concatenating the times copies of this seq. 501 | * 502 | * @param times times to repeat 503 | * @return the new seq 504 | * @throws IllegalArgumentException if times <= 0 505 | */ 506 | Seq repeat(int times); 507 | 508 | /** 509 | * Returns a copy of the seq itself with all null elements removed. 510 | * 511 | * @return the new seq with all null elements removed 512 | */ 513 | Seq compact(); 514 | 515 | /** 516 | * Returns the number of the specified element. 517 | * 518 | * @param element the element to countIf 519 | * @return the number of the specified element 520 | */ 521 | default int count(T element) { 522 | int count = 0; 523 | for (int i = 0; i < size(); i++) { 524 | if (element == null) { 525 | if (this.get(i) == null) 526 | count++; 527 | } else { 528 | if (this.get(i) != null && this.get(i).equals(element)) 529 | count++; 530 | } 531 | } 532 | return count; 533 | } 534 | 535 | /** 536 | * Returns the number of elements which satisfy the condition. 537 | * 538 | * @param condition the condition used to filter elements by passing the element, 539 | * returns true if the element satisfies the condition, otherwise returns false. 540 | * @return the number of elements which satisfy the condition 541 | * @throws NullPointerException if condition is null 542 | */ 543 | default int countIf(Predicate condition) { 544 | Objects.requireNonNull(condition); 545 | int count = 0; 546 | for (int i = 0; i < size(); i++) { 547 | if (condition.test(this.get(i))) 548 | count++; 549 | } 550 | return count; 551 | } 552 | 553 | /** 554 | * Returns the number of elements which satisfy the condition. 555 | *

556 | * Similar to {@link #countIf(Predicate)}, with additional parameter "index" as the second parameter of the lambda expression. 557 | *

558 | * 559 | * @param condition the condition used to filter elements by passing the element and its index, 560 | * returns true if the element satisfies the condition, otherwise returns false. 561 | * @return the number of elements which satisfy the condition 562 | * @throws NullPointerException if condition is null 563 | */ 564 | default int countIf(BiPredicate condition) { 565 | Objects.requireNonNull(condition); 566 | int count = 0; 567 | for (int i = 0; i < size(); i++) { 568 | if (condition.test(this.get(i), i)) 569 | count++; 570 | } 571 | return count; 572 | } 573 | 574 | /** 575 | * Returns the element at index. A negative index counts from the end of self. 576 | * 577 | * @param index index of the element to return 578 | * @return the element at the specified position in this seq 579 | * @throws IndexOutOfBoundsException if the index is out of range 580 | * (index >= size() || index < -size()) 581 | */ 582 | T get(int index); 583 | 584 | /** 585 | * Returns the element at index. A negative index counts from the end of self. 586 | * If the index is out of range(index < -size() || index >= size()), a default value is returned. 587 | *

588 | * Similar to {@link #get(int)}. The difference between these two functions is how to solve the situation of illegal index.
589 | * {@link #get(int, Object)} returns a default value when the index is illegal.
590 | * {@link #get(int)} throws an exception({@link IndexOutOfBoundsException}) when the index is illegal. 591 | *

592 | * 593 | * @param index index of the element to return 594 | * @param defaultValue default value to return when the index is out of range 595 | * @return The element at the specified position in this seq, or default value if the index is out of range 596 | */ 597 | default T get(int index, T defaultValue) { 598 | if (index < -this.size() || index >= this.size()) { 599 | return defaultValue; 600 | } 601 | return this.get(index); 602 | } 603 | 604 | /** 605 | * Slices this seq to construct some slices in the original order, 606 | * each of slice contains n elements(only the last slice can contain less than n elements). 607 | * 608 | * @param n the number of elements in each slice except the last one 609 | * @return the collection of these slices 610 | * @throws IllegalArgumentException if n <= 0 611 | */ 612 | Seq> eachSlice(int n); 613 | 614 | /** 615 | * Slices the seq to construct some slices and take action on each slice. 616 | *

617 | * Similar to {@link #eachSlice(int)}, but instead of returning the slice collection, 618 | * it iterates the slice collection and takes action on each slice. 619 | *

620 | * 621 | * @param n the number of elements in each slice except the last one 622 | * @param action the action to take on each slice 623 | * @throws IllegalArgumentException if n <= 0 624 | * @throws NullPointerException if action is null 625 | */ 626 | default void forEachSlice(int n, Consumer> action) { 627 | Objects.requireNonNull(action); 628 | if (n <= 0) 629 | throw new IllegalArgumentException("n should be a positive number."); 630 | int size = this.size(); 631 | for (int i = 0; i < size; i += n) { 632 | action.accept(this.subSeq(i, i + n > size ? size : i + n)); 633 | } 634 | } 635 | 636 | /** 637 | * Performs a reduction on the elements of this seq, using the provided 638 | * binary operation, and returns the reduced value. 639 | * 640 | * @param accumulator the binary operation for combining two values 641 | * @return the result of the reduction, or null if the seq is empty 642 | * @throws NullPointerException if accumulator is null 643 | */ 644 | default T reduce(BinaryOperator accumulator) { 645 | Objects.requireNonNull(accumulator); 646 | boolean foundAny = false; 647 | T result = null; 648 | for (int i = 0; i < size(); i++) { 649 | if (!foundAny) { 650 | foundAny = true; 651 | result = this.get(i); 652 | } else 653 | result = accumulator.apply(result, this.get(i)); 654 | } 655 | return result; 656 | } 657 | 658 | /** 659 | * Performs a reduction on the elements of this seq, using the provided initial value 660 | * and a binary function, and returns the reduced value. 661 | *

662 | * Similar to {@link #reduce(BinaryOperator)}, 663 | * with an additional parameter "init" as the initial value of the reduction 664 | *

665 | * 666 | * @param init the initial value for the accumulating function 667 | * @param accumulator the binary function for combining two values 668 | * @return the result of the reduction 669 | * @throws NullPointerException if accumulator is null 670 | */ 671 | default R reduce(R init, BiFunction accumulator) { 672 | Objects.requireNonNull(accumulator); 673 | R result = init; 674 | for (int i = 0; i < size(); i++) 675 | result = accumulator.apply(result, this.get(i)); 676 | return result; 677 | } 678 | 679 | /** 680 | * Constructs a new seq containing all the elements of this seq in reverse order. 681 | * 682 | * @return the new seq with elements in reverse order 683 | */ 684 | Seq reverse(); 685 | 686 | /** 687 | * Similar with {@link #eachCombination(int)}, but instead of to return the seq, it iterates the seq and do action. 688 | */ 689 | void forEachCombination(int n, Consumer> action); 690 | 691 | 692 | /** 693 | * Return a new Seq of all combinations of length n of elements from this seq. 694 | * The implementation makes no guarantees about the order in which the combinations are returned. 695 | * This method uses the index as the identity of each element, thus it may return a combination with duplicated elements inside. 696 | * If you want to avoid this, use {@link #distinct()} before calling this method. 697 | */ 698 | Seq> eachCombination(int n); 699 | 700 | 701 | /** 702 | * Check whether any element of the seq satisfies the condition 703 | * 704 | * @throws NullPointerException if condition is null 705 | */ 706 | default boolean any(Predicate condition) { 707 | Objects.requireNonNull(condition); 708 | for (int i = 0; i < size(); i++) { 709 | if (condition.test(get(i))) { 710 | return true; 711 | } 712 | } 713 | return false; 714 | } 715 | 716 | /** 717 | * Check whether any element of the seq satisfies the condition 718 | * 719 | * @throws NullPointerException if condition is null 720 | */ 721 | default boolean any(BiPredicate condition) { 722 | Objects.requireNonNull(condition); 723 | for (int i = 0; i < size(); i++) { 724 | if (condition.test(get(i), i)) { 725 | return true; 726 | } 727 | } 728 | return false; 729 | } 730 | 731 | /** 732 | * Returns the index of the first occurrence of the specified element 733 | * in this list, or -1 if this list does not contain the element. 734 | * More formally, returns the lowest index i such that 735 | * (o==null ? get(i)==null : o.equals(get(i))), 736 | * or -1 if there is no such index. 737 | */ 738 | int indexOf(T t); 739 | 740 | /** 741 | * Returns the index of the last occurrence of the specified element 742 | * in this list, or -1 if this list does not contain the element. 743 | * More formally, returns the highest index i such that 744 | * (o==null ? get(i)==null : o.equals(get(i))), 745 | * or -1 if there is no such index. 746 | */ 747 | int lastIndexOf(T t); 748 | 749 | /** 750 | * Check whether this seq contains the sub seq, if the given seq is empty, always return true. 751 | * 752 | * @throws NullPointerException if seq is null 753 | */ 754 | default boolean containsSubSeq(Seq seq) { 755 | return indexOfSubSeq(seq) != -1; 756 | } 757 | 758 | /** 759 | * Return the first index of the given sub seq, or -1 if the given seq is not a sub seq. 760 | * If the given seq is empty, always return 0. 761 | * 762 | * @throws NullPointerException if seq is null 763 | */ 764 | default int indexOfSubSeq(Seq seq) { 765 | Objects.requireNonNull(seq); 766 | 767 | if (seq.isEmpty()) { 768 | return 0; 769 | } 770 | 771 | if (size() < seq.size()) { 772 | return -1; 773 | } 774 | 775 | //Sunday algorithm 776 | 777 | Map lastIndex = new HashMap<>(); 778 | seq.forEach(lastIndex::put); 779 | 780 | int startI = 0, size = size(), len = seq.size(); 781 | while (size - startI >= len) { 782 | for (int i = 0; ; i++) { 783 | if (!get(startI + i).equals(seq.get(i))) { 784 | if (startI + len >= size) { 785 | return -1; 786 | } 787 | T next = get(startI + len); 788 | Integer last = lastIndex.get(next); 789 | if (last == null) { 790 | startI += len + 1; 791 | } else { 792 | startI += len - last; 793 | } 794 | break; 795 | } else if (i == len - 1) { 796 | return startI; 797 | } 798 | } 799 | } 800 | return -1; 801 | } 802 | 803 | /** 804 | * Return the last index of the given sub seq, or -1 if the given seq is not a sub seq. 805 | * If the given seq is empty, always return 0. 806 | * 807 | * @throws NullPointerException if seq is null 808 | */ 809 | default int lastIndexOfSubSeq(Seq seq) { 810 | Objects.requireNonNull(seq); 811 | 812 | if (seq.isEmpty()) { 813 | return 0; 814 | } 815 | 816 | if (size() < seq.size()) { 817 | return -1; 818 | } 819 | 820 | //Sunday algorithm 821 | 822 | Map lastIndex = new HashMap<>(); 823 | seq.forEachReverse(lastIndex::put); 824 | 825 | int size = size(), endI = size - 1, len = seq.size(); 826 | while (endI >= len - 1) { 827 | for (int i = 0; ; i++) { 828 | if (!get(endI - i).equals(seq.get(len - 1 - i))) { 829 | if (endI - len < 0) { 830 | return -1; 831 | } 832 | T next = get(endI - len); 833 | Integer last = lastIndex.get(next); 834 | if (last == null) { 835 | endI -= len + 1; 836 | } else { 837 | endI -= last + 1; 838 | } 839 | break; 840 | } else if (i == len - 1) { 841 | return endI - len + 1; 842 | } 843 | } 844 | } 845 | return -1; 846 | } 847 | 848 | /** 849 | * Computes the multiset intersection between this seq and another seq. 850 | * 851 | * @return A new seq which contains all elements of this seq which also appear in that, keeping the order of this seq. 852 | * If an element value x appears n times in that, then the first n occurrences of x will be retained in the result, but any following occurrences will be omitted. 853 | * @throws NullPointerException if the parameter seq is null 854 | */ 855 | Seq intersect(Seq seq); 856 | 857 | /** 858 | * Computes the multiset difference between this seq and another seq. 859 | * 860 | * @return A new seq which contains all elements of this seq except some of occurrences of elements that also appear in that, keeping the order of this seq. 861 | * If an element value x appears n times in that, then the first n occurrences of x will not form part of the result, but any following occurrences will. 862 | * @throws NullPointerException if the parameter seq is null 863 | */ 864 | Seq difference(Seq seq); 865 | 866 | /** 867 | * Check whether all elements of the seq satisfy the condition 868 | * 869 | * @throws NullPointerException if condition is null 870 | */ 871 | default boolean all(Predicate condition) { 872 | Objects.requireNonNull(condition); 873 | for (int i = 0; i < size(); i++) { 874 | if (!condition.test(get(i))) { 875 | return false; 876 | } 877 | } 878 | return true; 879 | } 880 | 881 | /** 882 | * Check whether all elements of the seq satisfy the condition 883 | * 884 | * @throws NullPointerException if condition is null 885 | */ 886 | default boolean all(BiPredicate condition) { 887 | Objects.requireNonNull(condition); 888 | for (int i = 0; i < size(); i++) { 889 | if (!condition.test(get(i), i)) { 890 | return false; 891 | } 892 | } 893 | return true; 894 | } 895 | 896 | /** 897 | * Check whether no element of the seq satisfies the condition 898 | * 899 | * @throws NullPointerException if condition is null 900 | */ 901 | default boolean none(Predicate condition) { 902 | Objects.requireNonNull(condition); 903 | for (int i = 0; i < size(); i++) { 904 | if (condition.test(get(i))) { 905 | return false; 906 | } 907 | } 908 | return true; 909 | } 910 | 911 | /** 912 | * Check whether no element of the seq satisfies the condition 913 | * 914 | * @throws NullPointerException if condition is null 915 | */ 916 | default boolean none(BiPredicate condition) { 917 | Objects.requireNonNull(condition); 918 | for (int i = 0; i < size(); i++) { 919 | if (condition.test(get(i), i)) { 920 | return false; 921 | } 922 | } 923 | return true; 924 | } 925 | 926 | /** 927 | * Returns the maximum element of the seq 928 | * 929 | * @throws NullPointerException if comparator is null 930 | */ 931 | default Optional max(Comparator comparator) { 932 | Objects.requireNonNull(comparator); 933 | 934 | if (size() == 0) { 935 | return Optional.empty(); 936 | } 937 | 938 | T candidate = get(0); 939 | for (int i = 1; i < size(); i++) { 940 | T current = get(i); 941 | if (comparator.compare(current, candidate) > 0) { 942 | candidate = current; 943 | } 944 | } 945 | 946 | return Optional.of(candidate); 947 | } 948 | 949 | /** 950 | * Returns the minimum element of the seq 951 | * 952 | * @throws NullPointerException if comparator is null 953 | */ 954 | default Optional min(Comparator comparator) { 955 | Objects.requireNonNull(comparator); 956 | 957 | if (size() == 0) { 958 | return Optional.empty(); 959 | } 960 | 961 | T candidate = get(0); 962 | for (int i = 1; i < size(); i++) { 963 | T current = get(i); 964 | if (comparator.compare(current, candidate) < 0) { 965 | candidate = current; 966 | } 967 | } 968 | 969 | return Optional.of(candidate); 970 | } 971 | 972 | /** 973 | * Swaps the elements at the specified positions in this seq. 974 | * 975 | * @param i the index of one element to be swapped. 976 | * @param j the index of the other element to be swapped. 977 | * @throws IndexOutOfBoundsException if either i or j 978 | * is out of range (i < 0 || i >= this.size() 979 | * || j < 0 || j >= this.size()). 980 | * @since 1.4 981 | */ 982 | Seq swap(int i, int j); 983 | 984 | /** 985 | * Rotates the elements in this seq by the specified distance. 986 | * After calling this method, the element at index i will be 987 | * the element previously at index (i - distance) mod 988 | * this.size(), for all values of i between 0 989 | * and list.size()-1, inclusive. (This method has no effect on 990 | * the size of the list.) 991 | * 992 | *

For example, suppose seq comprises [1, 2, 3, 4, 5]. 993 | * After invoking seq.rotate(1) (or 994 | * seq.rotate(-4)), list will comprise 995 | * [5, 1, 2, 3, 4]. 996 | * 997 | * @param distance the distance to rotate the seq. There are no 998 | * constraints on this value; it may be zero, negative, or 999 | * greater than this.size(). 1000 | */ 1001 | Seq rotate(int distance); 1002 | } 1003 | --------------------------------------------------------------------------------