├── codecov.yml ├── _config.yml ├── settings.gradle ├── .gitignore ├── .github └── images │ └── logo.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ └── java │ │ └── net │ │ └── jacobpeterson │ │ └── timeseriesdatastore │ │ ├── iterator │ │ └── DataSource.java │ │ ├── util │ │ ├── sort │ │ │ └── SortDirection.java │ │ └── temporalrange │ │ │ ├── TemporalRange.java │ │ │ └── TemporalRangeUtil.java │ │ └── database │ │ └── TimeSeriesDatabaseInterface.java └── test │ ├── resources │ └── logback.xml │ └── java │ └── net │ └── jacobpeterson │ └── timeseriesdatastore │ └── test │ └── util │ └── temporalrange │ └── TemporalRangeUtilTest.java ├── .travis.yml ├── LICENSE ├── .gitattributes ├── gradlew.bat ├── README.md ├── gradlew └── .editorconfig /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'TimeSeriesDataStore' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | .gradle/ 4 | .settings 5 | .DS_Store 6 | .idea/ 7 | -------------------------------------------------------------------------------- /.github/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/TimeSeriesDataStore/main/.github/images/logo.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/TimeSeriesDataStore/main/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/timeseriesdatastore/iterator/DataSource.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.timeseriesdatastore.iterator; 2 | 3 | /** 4 | * The {@link DataSource} enums defines the source type of data. 5 | */ 6 | public enum DataSource { 7 | 8 | /** The {@link DataSource} is from a database. */ 9 | DATABASE, 10 | 11 | /** The {@link DataSource} is from a datafeed. */ 12 | DATAFEED 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/timeseriesdatastore/util/sort/SortDirection.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.timeseriesdatastore.util.sort; 2 | 3 | /** 4 | * {@link SortDirection} defines enums that specify a sorting direction. 5 | */ 6 | public enum SortDirection { 7 | 8 | /** An ascending {@link SortDirection}. */ 9 | ASCENDING, 10 | 11 | /** A descending {@link SortDirection}. */ 12 | DESCENDING 13 | } 14 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %-5level %date{MM/dd/yyyy hh:mm:ss aa zzz} [%thread] %logger - %msg%n 5 | 6 | 7 | 8 | 9 | %-5level %date{MM/dd/yyyy hh:mm:ss aa zzz} [%thread] %caller{1} %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | install: true 3 | dist: trusty 4 | 5 | jdk: openjdk10 6 | 7 | before_script: 8 | - chmod +x gradlew 9 | 10 | script: 11 | # Install OpenJDK 14 12 | - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh 13 | - chmod +x $TRAVIS_BUILD_DIR/install-jdk.sh 14 | - export JAVA_HOME=$HOME/openjdk14 15 | - $TRAVIS_BUILD_DIR/install-jdk.sh -f 14 --target $JAVA_HOME 16 | 17 | - ./gradlew build check jacocoTestReport --stacktrace 18 | 19 | after_success: 20 | - bash <(curl -s https://codecov.io/bash) 21 | 22 | before_cache: 23 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 24 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 25 | 26 | cache: 27 | directories: 28 | - $HOME/.gradle/caches/ 29 | - $HOME/.gradle/wrapper/ 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Jacob Peterson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://www.gitattributes.io/api/java 2 | # 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | # These files are text and should be normalized (Convert crlf => lf) 11 | *.css text 12 | *.df text 13 | *.htm text 14 | *.html text 15 | *.java text 16 | *.js text 17 | *.json text 18 | *.jsp text 19 | *.jspf text 20 | *.jspx text 21 | *.properties text 22 | *.sh text 23 | *.tld text 24 | *.txt text 25 | *.tag text 26 | *.tagx text 27 | *.xml text 28 | *.yml text 29 | 30 | # Declare files that will always have CRLF line endings on checkout. 31 | *.sln text eol=crlf 32 | *.bat text eol=crlf 33 | 34 | # These files are binary and should be left untouched 35 | # (binary is a macro for -text -diff) 36 | *.class binary 37 | *.dll binary 38 | *.ear binary 39 | *.gif binary 40 | *.ico binary 41 | *.jar binary 42 | *.jpg binary 43 | *.jpeg binary 44 | *.png binary 45 | *.so binary 46 | *.war binary 47 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/timeseriesdatastore/util/temporalrange/TemporalRange.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.timeseriesdatastore.util.temporalrange; 2 | 3 | import java.time.temporal.Temporal; 4 | import java.util.Objects; 5 | 6 | /** 7 | * {@link TemporalRange} represents a {@link Temporal} a from and a to time. 8 | */ 9 | public class TemporalRange { 10 | 11 | private T from; 12 | private T to; 13 | 14 | /** 15 | * Instantiates a new {@link TemporalRange}. 16 | * 17 | * @param from the 'from' 18 | * @param to the 'to' 19 | */ 20 | public TemporalRange(T from, T to) { 21 | this.from = from; 22 | this.to = to; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object other) { 27 | if (this == other) { 28 | return true; 29 | } 30 | if (other == null || getClass() != other.getClass()) { 31 | return false; 32 | } 33 | 34 | TemporalRange otherTemporalRange = (TemporalRange) other; 35 | return Objects.equals(from, otherTemporalRange.from) && Objects.equals(to, otherTemporalRange.to); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "TemporalRange{" + 41 | "from=" + from + 42 | ", to=" + to + 43 | '}'; 44 | } 45 | 46 | /** 47 | * Gets the {@link #from}. 48 | * 49 | * @return the {@link #from} 50 | */ 51 | public T getFrom() { 52 | return from; 53 | } 54 | 55 | /** 56 | * Sets the {@link #from}. 57 | * 58 | * @param from the {@link #from} 59 | */ 60 | public void setFrom(T from) { 61 | this.from = from; 62 | } 63 | 64 | /** 65 | * Gets the {@link #to}. 66 | * 67 | * @return the {@link #to} 68 | */ 69 | public T getTo() { 70 | return to; 71 | } 72 | 73 | /** 74 | * Sets the {@link #to}. 75 | * 76 | * @param to the {@link #to} 77 | */ 78 | public void setTo(T to) { 79 | this.to = to; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

TimeSeriesDataStore Logo

2 |

3 | Maven Central 4 | Javadocs 5 | Build Status 6 | CodeCov 7 | MIT License 8 |

9 | 10 | # Overview 11 | TimeSeriesDataStore is a Java API to store and iterate over time-series data retrieved from a data feed and stored in a database. Data from a data feed is fetched if data within a requested time range is not in a database. This data is then stored in a database for future requests. Data from a database is fetched if data within a requested time range is in a database, thus serving data much quicker than the data feed. 12 | 13 | # Gradle and Maven Integration 14 | If you are using Gradle as your build tool, add the following dependency to your `build.gradle` file: 15 | 16 | ``` 17 | dependencies { 18 | implementation group: 'net.jacobpeterson', name: 'timeseriesdatastore', version: '1.0' 19 | } 20 | ``` 21 | 22 | If you are using Maven as your build tool, add the following dependency to your `pom.xml` file: 23 | 24 | ``` 25 | 26 | net.jacobpeterson 27 | timeseriesdatastore 28 | 1.0 29 | 30 | ``` 31 | 32 | # Logger 33 | For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/). 34 | 35 | # Contributing 36 | Contributions are welcome! 37 | 38 | If you are creating a Pull Request, be sure to create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `main` branch in your fork. 39 | -------------------------------------------------------------------------------- /src/test/java/net/jacobpeterson/timeseriesdatastore/test/util/temporalrange/TemporalRangeUtilTest.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.timeseriesdatastore.test.util.temporalrange; 2 | 3 | import net.jacobpeterson.timeseriesdatastore.util.temporalrange.TemporalRange; 4 | import net.jacobpeterson.timeseriesdatastore.util.temporalrange.TemporalRangeUtil; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.time.Duration; 11 | import java.time.LocalDateTime; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * Unit tests for {@link TemporalRangeUtil}. 17 | */ 18 | public class TemporalRangeUtilTest { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(TemporalRangeUtilTest.class); 21 | 22 | /** 23 | * Tests {@link TemporalRangeUtil#getMissingTemporalRanges(LocalDateTime, LocalDateTime, List, Duration, Duration)} 24 | * with normal arguments. 25 | */ 26 | @Test 27 | public void testGetMissingTemporalRangesNormal() { 28 | LocalDateTime rangeFrom = LocalDateTime.of(2021, 1, 1, 0, 0); 29 | LocalDateTime rangeTo = LocalDateTime.of(2021, 1, 7, 0, 0); 30 | 31 | List> validTemporalRanges = new ArrayList<>(); 32 | validTemporalRanges.add(new TemporalRange<>( 33 | LocalDateTime.of(2021, 1, 1, 1, 0), LocalDateTime.of(2021, 1, 3, 12, 30))); 34 | validTemporalRanges.add(new TemporalRange<>( 35 | LocalDateTime.of(2021, 1, 2, 0, 0), LocalDateTime.of(2021, 1, 4, 0, 0))); 36 | validTemporalRanges.add(new TemporalRange<>( 37 | LocalDateTime.of(2021, 1, 5, 9, 0), LocalDateTime.of(2021, 1, 6, 1, 0))); 38 | validTemporalRanges.add(new TemporalRange<>( 39 | LocalDateTime.of(2021, 1, 6, 4, 0), LocalDateTime.of(2021, 1, 6, 8, 0))); 40 | 41 | List> expectedMissingTemporalRanges = new ArrayList<>(); 42 | expectedMissingTemporalRanges.add(new TemporalRange<>( 43 | LocalDateTime.of(2021, 1, 1, 0, 0), LocalDateTime.of(2021, 1, 1, 1, 0))); 44 | expectedMissingTemporalRanges.add(new TemporalRange<>( 45 | LocalDateTime.of(2021, 1, 4, 0, 0), LocalDateTime.of(2021, 1, 5, 9, 0))); 46 | expectedMissingTemporalRanges.add(new TemporalRange<>( 47 | LocalDateTime.of(2021, 1, 6, 1, 0), LocalDateTime.of(2021, 1, 6, 4, 0))); 48 | expectedMissingTemporalRanges.add(new TemporalRange<>( 49 | LocalDateTime.of(2021, 1, 6, 8, 0), LocalDateTime.of(2021, 1, 7, 0, 0))); 50 | 51 | List> actualMissingTemporalRanges = 52 | TemporalRangeUtil.getMissingTemporalRanges(rangeFrom, rangeTo, validTemporalRanges, null, null); 53 | 54 | LOGGER.debug("Valid: {}", validTemporalRanges); 55 | LOGGER.debug("Expected: {}", expectedMissingTemporalRanges); 56 | LOGGER.debug("Actual: {}", actualMissingTemporalRanges); 57 | 58 | Assertions.assertEquals(expectedMissingTemporalRanges, actualMissingTemporalRanges); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/timeseriesdatastore/database/TimeSeriesDatabaseInterface.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.timeseriesdatastore.database; 2 | 3 | import net.jacobpeterson.timeseriesdatastore.util.sort.SortDirection; 4 | import net.jacobpeterson.timeseriesdatastore.util.temporalrange.TemporalRange; 5 | import org.jooq.Condition; 6 | import org.jooq.Cursor; 7 | import org.jooq.DSLContext; 8 | import org.jooq.OrderField; 9 | import org.jooq.Record; 10 | import org.jooq.Record3; 11 | import org.jooq.RecordMapper; 12 | import org.jooq.RecordUnmapper; 13 | import org.jooq.Table; 14 | import org.jooq.TableField; 15 | import org.jooq.exception.DataAccessException; 16 | import org.jooq.impl.SQLDataType; 17 | 18 | import java.sql.Time; 19 | import java.time.LocalDateTime; 20 | import java.time.LocalTime; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | 24 | import static com.google.common.base.Preconditions.checkArgument; 25 | import static org.jooq.impl.DSL.trueCondition; 26 | import static org.jooq.impl.DSL.val; 27 | 28 | /** 29 | * {@link TimeSeriesDatabaseInterface} is used to access database time series tables. Specifically, a time series data 30 | * table and a timestamp ranges table. The data table contains records with time series data (data with a timestamp 31 | * field). The timestamp ranges table contains records that define the valid time ranges that exist in the time series 32 | * data table. Not only does this provide fast lookup for what date ranges do not exist in the data table, but it also 33 | * defines what timestamp ranges have already been fetched by the data feed. 34 | * 35 | * @param the type parameter of the key used for the time series data table and the timestamp ranges table 36 | * @param the time series data {@link Record} type parameter 37 | * @param

the time series data POJO type parameter 38 | * @param the timestamp {@link Record3} type parameter (value1 being the table key, value2 being the 'from' and 39 | * value3 being the 'to') 40 | */ 41 | public abstract class TimeSeriesDatabaseInterface> { 43 | 44 | protected DSLContext create; 45 | 46 | /** 47 | * Instantiates a new {@link TimeSeriesDatabaseInterface}. 48 | * 49 | * @param create the {@link DSLContext} 50 | */ 51 | public TimeSeriesDatabaseInterface(DSLContext create) { 52 | this.create = create; 53 | } 54 | 55 | /** 56 | * Gets the time series data {@link Table}. 57 | * 58 | * @return the time series data {@link Table} 59 | */ 60 | protected abstract Table getDataTable(); 61 | 62 | /** 63 | * Gets the time series data 'key' {@link TableField}. 64 | * 65 | * @return the time series data 'key' {@link TableField} 66 | */ 67 | protected abstract TableField getDataKeyTableField(); 68 | 69 | /** 70 | * Gets the time series data 'timestamp' {@link TableField}. 71 | * 72 | * @return the time series data 'timestamp' {@link TableField} 73 | */ 74 | protected abstract TableField getDataTimestampTableField(); 75 | 76 | /** 77 | * Gets the time series data {@link Record} to POJO {@link RecordMapper}. 78 | * 79 | * @return the {@link Record} to POJO {@link RecordMapper} 80 | */ 81 | protected abstract RecordMapper getDataRecordMapper(); 82 | 83 | /** 84 | * Gets the POJO to {@link Record} {@link RecordUnmapper}. 85 | * 86 | * @return the the POJO to {@link Record} {@link RecordUnmapper} 87 | */ 88 | protected abstract RecordUnmapper getDataRecordUnmapper(); 89 | 90 | /** 91 | * Gets the time series data timestamp ranges {@link Table}. 92 | * 93 | * @return the time series data timestamp ranges {@link Table} 94 | */ 95 | protected abstract Table getTimestampRangesTable(); 96 | 97 | /** 98 | * Gets the time series data timestamp ranges 'key' {@link TableField}. 99 | * 100 | * @return the time series data timestamp ranges 'key' {@link TableField} 101 | */ 102 | protected abstract TableField getTimestampRangesKeyTableField(); 103 | 104 | /** 105 | * Gets the timestamp ranges 'from' timestamp {@link TableField}. 106 | * 107 | * @return the timestamp ranges 'from' timestamp {@link TableField} 108 | */ 109 | protected abstract TableField getTimestampRangesFromTableField(); 110 | 111 | /** 112 | * Gets the timestamp ranges 'to' timestamp {@link TableField}. 113 | * 114 | * @return the timestamp ranges 'to' timestamp {@link TableField} 115 | */ 116 | protected abstract TableField getTimestampRangesToTableField(); 117 | 118 | /** 119 | * Returns the amount of rows that a data query should fetch at one time. This is used for the {@link #get(Object, 120 | * LocalDateTime, LocalDateTime, LocalTime, LocalTime, SortDirection)} method. 121 | * 122 | * @return the fetch row size 123 | */ 124 | protected abstract int getDataFetchSize(); 125 | 126 | /** 127 | * Inserts a time series data POJO into the {@link #getDataTable()}. 128 | * 129 | * @param dataPOJO the data POJO 130 | * 131 | * @throws DataAccessException thrown for {@link DataAccessException}s 132 | */ 133 | public void insert(P dataPOJO) throws DataAccessException { 134 | checkArgument(dataPOJO != null, "The data POJO cannot be null!"); 135 | 136 | // Insert the converted POJO to table record into the database data table 137 | create.insertInto(getDataTable()) 138 | .set(getDataRecordUnmapper().unmap(dataPOJO)) 139 | .onDuplicateKeyIgnore() 140 | .execute(); 141 | } 142 | 143 | /** 144 | * Gets the data POJOs from this database. 145 | * 146 | * @param key the key 147 | * @param from the 'from' (inclusive) 148 | * @param to the 'to' (exclusive) 149 | * @param beginFilterTime allows you to specify the earliest time of day for which to receive data (null for no 150 | * filter) (inclusive) 151 | * @param endFilterTime allows you to specify the latest time of day for which to receive data (null for no 152 | * filter) (exclusive) 153 | * @param sortDirection the {@link SortDirection} (defaults to {@link SortDirection#ASCENDING}) 154 | * 155 | * @return a lazy {@link Iterator} (that is, an {@link Iterator} that fetches {@link #getDataFetchSize()} rows at a 156 | * time) 157 | * 158 | * @throws DataAccessException thrown for {@link DataAccessException}s 159 | */ 160 | public Iterator

get(K key, LocalDateTime from, LocalDateTime to, LocalTime beginFilterTime, 161 | LocalTime endFilterTime, SortDirection sortDirection) 162 | throws DataAccessException { 163 | // Check arguments 164 | checkArgument(key != null, "Key cannot be null!"); 165 | checkArgument(from != null, "From cannot be null!"); 166 | checkArgument(to != null, "To cannot be null!"); 167 | sortDirection = sortDirection == null ? SortDirection.ASCENDING : sortDirection; 168 | 169 | // Create WHERE clause conditions 170 | Condition keyEqualCondition = getDataKeyTableField().equal(key); 171 | Condition timestampRangeCondition = getDataTimestampTableField().greaterOrEqual(from) 172 | .and(getDataTimestampTableField().lessThan(to)); 173 | Condition timestampFilterCondition = trueCondition(); 174 | if (beginFilterTime != null) { 175 | timestampFilterCondition = timestampFilterCondition.and(getDataTimestampTableField().cast(SQLDataType.TIME) 176 | .greaterOrEqual(Time.valueOf(beginFilterTime))); 177 | } 178 | if (endFilterTime != null) { 179 | timestampFilterCondition = timestampFilterCondition.and(getDataTimestampTableField().cast(SQLDataType.TIME) 180 | .lessThan(Time.valueOf(endFilterTime))); 181 | } 182 | 183 | // Create ORDER BY clause 184 | OrderField orderByField = sortDirection == SortDirection.ASCENDING ? 185 | getDataTimestampTableField().asc() : 186 | getDataTimestampTableField().desc(); 187 | 188 | final RecordMapper dataRecordMapper = getDataRecordMapper(); 189 | final Cursor recordCursor = create.selectFrom(getDataTable()) 190 | .where(keyEqualCondition.and(timestampRangeCondition).and(timestampFilterCondition)) 191 | .orderBy(orderByField) 192 | .fetchSize(getDataFetchSize()) 193 | .fetchLazy(); 194 | 195 | return new Iterator

() { 196 | @Override 197 | public boolean hasNext() { 198 | return recordCursor.hasNext(); // Closes the database query cursor automatically 199 | } 200 | 201 | @Override 202 | public P next() { 203 | return recordCursor.fetchNext(dataRecordMapper); 204 | } 205 | }; 206 | } 207 | 208 | /** 209 | * Inserts a timestamp range {@link Record3} into the {@link #getTimestampRangesTable()} table (does nothing if it 210 | * already exists). Note that only {@link TemporalRange}s that are completely filled in the underlying data table 211 | * should be inserted into the {@link #getTimestampRangesTable()} table. 212 | * 213 | * @param key the key 214 | * @param from the 'from' whose value should be treated inclusively 215 | * @param to the 'to' whose value should be treated exclusively 216 | * 217 | * @throws DataAccessException thrown for {@link DataAccessException}s 218 | */ 219 | public void insertTimestampRangeRecord(K key, LocalDateTime from, LocalDateTime to) throws DataAccessException { 220 | // Check arguments 221 | checkArgument(key != null, "Key cannot be null!"); 222 | checkArgument(from != null, "From cannot be null!"); 223 | checkArgument(to != null, "To cannot be null!"); 224 | 225 | // Create the record 226 | Record3 timestampRangesRecord = create.newRecord(getTimestampRangesTable()); 227 | timestampRangesRecord.set(getTimestampRangesKeyTableField(), key); 228 | timestampRangesRecord.set(getTimestampRangesFromTableField(), from); 229 | timestampRangesRecord.set(getTimestampRangesToTableField(), to); 230 | 231 | // Insert into the table (do nothing if it already exists) 232 | create.insertInto(getTimestampRangesTable()) 233 | .set(timestampRangesRecord) 234 | .onDuplicateKeyIgnore() 235 | .execute(); 236 | } 237 | 238 | /** 239 | * Gets {@link TemporalRange}s that were inserted via {@link #insertTimestampRangeRecord(Object, LocalDateTime, 240 | * LocalDateTime)} given a {@link LocalDateTime} range. 241 | * 242 | * @param key the key 243 | * @param from the 'from' (inclusive) 244 | * @param to the 'to' (inclusive) 245 | * @param sortDirection the {@link SortDirection} (defaults to {@link SortDirection#ASCENDING}) 246 | * 247 | * @return a {@link List} of {@link TemporalRange}s of type {@link LocalDateTime}. Note that the {@link 248 | * LocalDateTime}s in the {@link TemporalRange}s could go beyond the range of the 'from' and the 'to' and is added 249 | * to the {@link List} as long as one of the {@link LocalDateTime}s is in the given range. This {@link List} is 250 | * sorted by the 'from' and then the 'to' if sortDirection is {@link SortDirection#ASCENDING}, otherwise it is 251 | * sorted by the 'to' and then the 'from'. 252 | * 253 | * @throws DataAccessException thrown for {@link DataAccessException}s 254 | */ 255 | public List> getTimestampRanges(K key, LocalDateTime from, LocalDateTime to, 256 | SortDirection sortDirection) throws DataAccessException { 257 | // Check arguments 258 | checkArgument(key != null, "Key cannot be null!"); 259 | checkArgument(from != null, "From cannot be null!"); 260 | checkArgument(to != null, "To cannot be null!"); 261 | sortDirection = sortDirection == null ? SortDirection.ASCENDING : sortDirection; 262 | 263 | // Create WHERE clause conditions 264 | // We basically want to get any timestamp range that intersects, is contained within, or contains the 265 | // passed in timestamp range. 266 | Condition keyEqualCondition = getTimestampRangesKeyTableField().equal(key); 267 | Condition fromArgumentCondition = val(from).greaterOrEqual(getTimestampRangesFromTableField()).and( 268 | val(from).lessOrEqual(getTimestampRangesToTableField())); 269 | Condition fromDatabaseCondition = getTimestampRangesFromTableField().greaterOrEqual(from).and( 270 | getTimestampRangesToTableField().lessOrEqual(from)); 271 | Condition toArgumentCondition = val(to).lessOrEqual(getTimestampRangesToTableField()).and( 272 | val(from).greaterOrEqual(getTimestampRangesToTableField())); 273 | Condition toDatabaseCondition = getTimestampRangesToTableField().lessOrEqual(to).and( 274 | getTimestampRangesToTableField().greaterOrEqual(from)); 275 | 276 | // Create ORDER BY clause 277 | // We first want to order by the 'from' ASC and then by the 'to' ASC if the sortDirection is 'ASCENDING' and 278 | // order by the 'to' DESC and then by the 'from' DESC if the sortDirection is 'DESCENDING'. 279 | OrderField firstOrder = sortDirection == SortDirection.ASCENDING ? 280 | getTimestampRangesFromTableField().asc() : 281 | getTimestampRangesToTableField().desc(); 282 | OrderField secondOrder = sortDirection == SortDirection.ASCENDING ? 283 | getTimestampRangesToTableField().asc() : 284 | getTimestampRangesFromTableField().desc(); 285 | 286 | return create 287 | .select(getTimestampRangesFromTableField(), getTimestampRangesToTableField()) 288 | .from(getTimestampRangesTable()) 289 | .where(keyEqualCondition.and( 290 | fromArgumentCondition 291 | .or(fromDatabaseCondition) 292 | .or(toArgumentCondition) 293 | .or(toDatabaseCondition))) 294 | .orderBy(firstOrder, secondOrder) 295 | .fetch(record -> new TemporalRange<>(record.value1(), record.value2())); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/timeseriesdatastore/util/temporalrange/TemporalRangeUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.timeseriesdatastore.util.temporalrange; 2 | 3 | import java.time.Duration; 4 | import java.time.LocalDateTime; 5 | import java.time.LocalTime; 6 | import java.util.ArrayList; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | 10 | import static com.google.common.base.Preconditions.checkArgument; 11 | import static com.google.common.base.Preconditions.checkNotNull; 12 | 13 | /** 14 | * {@link TemporalRangeUtil} contains several utility methods for date/time objects and for {@link TemporalRange}s. 15 | */ 16 | public final class TemporalRangeUtil { 17 | 18 | /** 19 | * Gets missing {@link TemporalRange}s from found/valid {@link TemporalRange}s. That is, invert {@link 20 | * TemporalRange}s given in the validTemporalRanges on a timeline. 21 | * 22 | * @param from the 'from' {@link LocalDateTime} (inclusive) 23 | * @param to the 'to' {@link LocalDateTime} (inclusive) 24 | * @param validTemporalRanges the valid {@link TemporalRange}s (the {@link TemporalRange#getFrom()}s MUST be 25 | * sorted from oldest to newest) (this method infers that the {@link 26 | * TemporalRange#getFrom()} and {@link TemporalRange#getTo()} in the {@link 27 | * TemporalRange} are inclusive of their associated {@link LocalDateTime}s) 28 | * @param fromExclusivityOffset added to a {@link TemporalRange#getFrom()} {@link LocalDateTime} in a returned 29 | * {@link TemporalRange} to enable exclusivity for {@link TemporalRange}s 30 | * (null for no offset) 31 | * @param toExclusivityOffset subtracted from a {@link TemporalRange#getTo()} {@link LocalDateTime} in a returned 32 | * {@link TemporalRange} to enable exclusivity for {@link TemporalRange}s 33 | * (null for no offset) 34 | * 35 | * @return a {@link List} of {@link TemporalRange}s 36 | */ 37 | public static ArrayList> getMissingTemporalRanges( 38 | LocalDateTime from, LocalDateTime to, List> validTemporalRanges, 39 | Duration fromExclusivityOffset, Duration toExclusivityOffset) { 40 | checkNotNull(from); 41 | checkNotNull(to); 42 | checkArgument(!to.isBefore(from), "From must be before to!"); // Checks that to >= from 43 | 44 | // Create an empty list if null was passed in 45 | validTemporalRanges = validTemporalRanges == null ? new ArrayList<>() : validTemporalRanges; 46 | 47 | // Set defaults for exclusivity offsets if necessary 48 | fromExclusivityOffset = fromExclusivityOffset == null ? Duration.ZERO : fromExclusivityOffset; 49 | toExclusivityOffset = toExclusivityOffset == null ? Duration.ZERO : toExclusivityOffset; 50 | 51 | // Note that we do not check if the passed in 'from + fromExclusivityOffset' and 'to - toExclusivityOffset' 52 | // overlap because the below logic accounts for overlapping of these 'from' and 'to' offsets. 53 | // This would occur if the 'from' and 'to' are the same and the offsets are not zero. 54 | 55 | // If validTemporalRanges is empty, return the input from and to 56 | if (validTemporalRanges.isEmpty()) { 57 | ArrayList> missingTimestamp = new ArrayList<>(); 58 | missingTimestamp.add(new TemporalRange<>(from, to)); 59 | return missingTimestamp; 60 | } 61 | 62 | final LocalDateTime fromPlusFromOffset = from.plus(fromExclusivityOffset); 63 | final ArrayList> missingTimestampRanges = new ArrayList<>(); 64 | LocalDateTime missingFrom = from; 65 | 66 | // Loop through the validTemporalRanges from oldest to newest 67 | for (TemporalRange validFromTo : validTemporalRanges) { 68 | LocalDateTime validFrom = validFromTo.getFrom(); 69 | LocalDateTime validTo = validFromTo.getTo(); 70 | 71 | if (validFrom.isAfter(validTo)) { 72 | throw new IllegalArgumentException("From must be before to in the validTemporalRanges!"); 73 | } 74 | 75 | if (validFrom.isBefore(missingFrom) || validFrom.equals(missingFrom)) { 76 | if (validTo.isAfter(from)) { 77 | missingFrom = validTo; 78 | } else if (validTo.equals(from)) { 79 | // If validTo equals the passed in 'from' then add fromOffset to missingFrom 80 | missingFrom = fromPlusFromOffset; 81 | } 82 | // else do nothing because validTo is before passed in 'from' so 83 | // the validFromTo range is before the passed in range 84 | 85 | } else { // validFrom is after missingFrom 86 | 87 | // Break if missingFrom/previous validTo is beyond the passed in 'to' 88 | // or if the validFrom is after the passed in 'to' 89 | if (missingFrom.isAfter(to) || validFrom.isAfter(to)) { 90 | break; 91 | } 92 | 93 | // Create missingFrom and missingTo 'LocalDateTime's with offsets 94 | LocalDateTime missingFromPlusFromOffset = missingFrom.plus(fromExclusivityOffset); 95 | LocalDateTime missingToMinusToOffset = validFrom.minus(toExclusivityOffset); 96 | // If offsets cause missingFromTo TemporalRange to overlap, do not add the missingFromTo TemporalRange 97 | // (but it is okay if they are equal) 98 | if (!missingFromPlusFromOffset.isAfter(missingToMinusToOffset)) { 99 | // Clamp offsets to passed in 'from' and passed in 'to' just in case 100 | missingFromPlusFromOffset = min(missingFromPlusFromOffset, to); 101 | missingToMinusToOffset = max(missingToMinusToOffset, from); 102 | 103 | missingTimestampRanges.add(new TemporalRange<>( 104 | // Add the missingFromTo TemporalRange with offset, but if the missingFrom is equal to the 105 | // passed in 'from' then don't add the fromOffset to the missingFrom because 106 | // it should be inclusive if there is no valid range that includes the passed in 'from'. 107 | missingFrom.equals(from) ? missingFrom : missingFromPlusFromOffset, 108 | missingToMinusToOffset)); 109 | } 110 | 111 | missingFrom = validTo; 112 | 113 | // Break if validTo is beyond the passed in 'to' 114 | if (validTo.isAfter(to) || validTo.equals(to)) { 115 | break; 116 | } 117 | } 118 | } 119 | 120 | // Clamp missingFrom to 'from' (without fromOffset) 121 | if (missingFrom.isBefore(from)) { 122 | missingFrom = from; 123 | } 124 | 125 | // Add the last missingFrom and passed in 'to' (without toOffset) TemporalRange if 126 | // no validTimestamps extend beyond the passed in 'to' 127 | if (missingFrom.isBefore(to)) { 128 | LocalDateTime missingFromPlusFromOffset = missingFrom.plus(fromExclusivityOffset); 129 | missingTimestampRanges.add(new TemporalRange<>( 130 | missingFrom.equals(from) ? missingFrom : missingFromPlusFromOffset, 131 | to)); 132 | } 133 | 134 | return missingTimestampRanges; 135 | } 136 | 137 | /** 138 | * Squashes the given {@link TemporalRange}s on the timeline. That is, any overlapping {@link TemporalRange}s become 139 | * one {@link TemporalRange}. 140 | * 141 | * @param temporalRanges the {@link List} of {@link TemporalRange}s (the {@link TemporalRange#getFrom()}s 142 | * MUST be sorted from oldest to newest) 143 | * 144 | * @return a squashed {@link TemporalRange}s {@link List} 145 | */ 146 | public static List> squash(List> temporalRanges) { 147 | if (temporalRanges == null || temporalRanges.isEmpty()) { 148 | return temporalRanges; 149 | } 150 | 151 | ArrayList> squashedTemporalRanges = new ArrayList<>(); 152 | 153 | LocalDateTime currentOldestFrom = temporalRanges.get(0).getFrom(); 154 | LocalDateTime currentNewestTo = temporalRanges.get(0).getTo(); 155 | for (int index = 1; index < temporalRanges.size(); index++) { // Start the loop at the 2nd TemporalRange 156 | TemporalRange currentTemporalRange = temporalRanges.get(index); 157 | 158 | // Check if the currentTemporalRange 'from' > the 'newestTo' 159 | if (currentTemporalRange.getFrom().isAfter(currentNewestTo)) { 160 | squashedTemporalRanges.add(new TemporalRange<>(currentOldestFrom, currentNewestTo)); 161 | currentOldestFrom = currentTemporalRange.getFrom(); 162 | currentNewestTo = currentTemporalRange.getTo(); 163 | 164 | // Check if currentTemporalRange 'to' > the 'newestTo' 165 | } else if (currentTemporalRange.getTo().isAfter(currentNewestTo)) { 166 | currentNewestTo = currentTemporalRange.getTo(); 167 | } 168 | } 169 | 170 | // Add the last squashed TemporalRange (to also account for there only being one TemporalRange in the 171 | // passed-in list OR that there was only be one squashed TemporalRange) 172 | squashedTemporalRanges.add(new TemporalRange<>(currentOldestFrom, currentNewestTo)); 173 | 174 | return squashedTemporalRanges; 175 | } 176 | 177 | /** 178 | * Clamps the given {@link TemporalRange}s on the timeline. That is, any {@link TemporalRange}s that lie outside the 179 | * passed in from and to are clamped to the passed in from and 180 | * to. 181 | * 182 | * @param temporalRanges the {@link List} of {@link TemporalRange}s (the {@link TemporalRange#getFrom()}s 183 | * MUST be sorted from oldest to newest) 184 | * @param from the 'from' {@link LocalDateTime} to clamp to 185 | * @param to the 'to' {@link LocalDateTime} to clamp to 186 | * 187 | * @return a clamped {@link TemporalRange} {@link List} 188 | */ 189 | public static List> clamp(List> temporalRanges, 190 | LocalDateTime from, LocalDateTime to) { 191 | if (temporalRanges == null || temporalRanges.isEmpty()) { 192 | return temporalRanges; 193 | } 194 | 195 | checkArgument(!to.isBefore(from), "From must be before to!"); // Checks that to >= from 196 | 197 | ArrayList> clampedTemporalRanges = new ArrayList<>(); 198 | 199 | for (TemporalRange currentTemporalRange : temporalRanges) { 200 | // Only add it if the TemporalRange is even within the 'from' and 'to' 201 | if (currentTemporalRange.getFrom().isAfter(to) || currentTemporalRange.getTo().isBefore(from)) { 202 | continue; 203 | } 204 | 205 | // Add the clamped TemporalRange 206 | clampedTemporalRanges.add(new TemporalRange<>(max(from, currentTemporalRange.getFrom()), 207 | min(to, currentTemporalRange.getTo()))); 208 | } 209 | 210 | return clampedTemporalRanges; 211 | } 212 | 213 | /** 214 | * Clamps the {@link TemporalRange}s on the timeline to {@link TemporalRange}s that are only within the given 215 | * from and to {@link LocalTime}s. This method assumes that NO {@link 216 | * TemporalRange}s passed in intersect with another. This can be done via {@link #squash(List)}. 217 | * 218 | * @param temporalRanges the {@link List} of {@link TemporalRange}s (the {@link TemporalRange#getFrom()}s 219 | * MUST be sorted from oldest to newest) 220 | * @param from the 'from' {@link LocalTime} to clamp to 221 | * @param to the 'to' {@link LocalTime} to clamp to 222 | * 223 | * @return a clamped {@link TemporalRange} {@link List} 224 | */ 225 | public static List> clamp(List> temporalRanges, 226 | LocalTime from, LocalTime to) { 227 | if (temporalRanges == null || temporalRanges.isEmpty()) { 228 | return temporalRanges; 229 | } 230 | 231 | from = from == null ? LocalTime.MIN : from; 232 | to = to == null ? LocalTime.MAX : to; 233 | 234 | checkArgument(!to.isBefore(from), "From must be before to!"); // Checks that to >= from 235 | 236 | ArrayList> clampedTemporalRanges = new ArrayList<>(); 237 | 238 | for (TemporalRange currentTemporalRange : temporalRanges) { 239 | LocalDateTime loopDateTime = currentTemporalRange.getFrom().with(LocalTime.MIN); 240 | 241 | // Loop while the 'loopDateTime' <= 'to' 242 | while (!loopDateTime.isAfter(currentTemporalRange.getTo())) { 243 | LocalDateTime loopDateTimeFrom = loopDateTime.with(from); 244 | LocalDateTime loopDateTimeTo = loopDateTime.with(to); 245 | 246 | LocalDateTime clampedFrom = max(currentTemporalRange.getFrom(), max(loopDateTimeFrom, loopDateTime)); 247 | loopDateTime = loopDateTime.plusDays(1).with(LocalTime.MIN); // Advance to start of the next day 248 | LocalDateTime clampedTo = min(currentTemporalRange.getTo(), min(loopDateTimeTo, loopDateTime)); 249 | 250 | // Add the time-clamped TemporalRange if it exists within the 'from' and 'to' bounds 251 | // AND the 'clampedFrom' != 'clampedTo'. 252 | if (!clampedTo.isBefore(loopDateTimeFrom) && !clampedFrom.isAfter(loopDateTimeTo) 253 | && !clampedFrom.equals(clampedTo)) { 254 | clampedTemporalRanges.add(new TemporalRange<>(clampedFrom, clampedTo)); 255 | } 256 | } 257 | } 258 | 259 | return clampedTemporalRanges; 260 | } 261 | 262 | /** 263 | * Gets the {@link LocalDateTime} that's the furthest in the future. 264 | * 265 | * @param a a {@link LocalDateTime} 266 | * @param b a {@link LocalDateTime} 267 | * 268 | * @return a {@link LocalDateTime} 269 | */ 270 | public static LocalDateTime max(LocalDateTime a, LocalDateTime b) { 271 | return a.isAfter(b) ? a : b; 272 | } 273 | 274 | /** 275 | * Gets the {@link LocalDateTime} that's the furthest in the past. 276 | * 277 | * @param a a {@link LocalDateTime} 278 | * @param b a {@link LocalDateTime} 279 | * 280 | * @return a {@link LocalDateTime} 281 | */ 282 | public static LocalDateTime min(LocalDateTime a, LocalDateTime b) { 283 | return a.isBefore(b) ? a : b; 284 | } 285 | 286 | /** 287 | * Checks if the {@link LocalDateTime} is in between the passed in from and to. 288 | * 289 | * @param dateTime the {@link LocalDateTime} to check 290 | * @param from the 'from' {@link LocalDateTime} 291 | * @param to the 'to' {@link LocalDateTime} 292 | * @param inclusive if true, then if the dateTime is equal to the from or the 293 | * to then this method will return true (aka inclusive of the range limits), 294 | * otherwise it's exclusive of the range limits. 295 | * 296 | * @return a boolean 297 | */ 298 | public static boolean isBetween(LocalDateTime dateTime, LocalDateTime from, LocalDateTime to, 299 | boolean inclusive) { 300 | if (inclusive && (dateTime.equals(from) || dateTime.equals(to))) { 301 | return true; 302 | } else { 303 | return dateTime.isAfter(from) && dateTime.isBefore(to); 304 | } 305 | } 306 | 307 | /** 308 | * Gets a comparator which will sort {@link TemporalRange}s by their {@link TemporalRange#getFrom()} and then by 309 | * their {@link TemporalRange#getTo()}. 310 | * 311 | * @return the {@link Comparator} 312 | */ 313 | public static Comparator> getFromToLocalDateTimeComparator() { 314 | return (range1, range2) -> { 315 | if (range1.getFrom().equals(range2.getFrom())) { 316 | return range1.getTo().compareTo(range2.getTo()); 317 | } else { 318 | return range1.getFrom().compareTo(range2.getFrom()); 319 | } 320 | }; 321 | } 322 | 323 | /** 324 | * Gets a comparator which will sort {@link TemporalRange}s by their {@link TemporalRange#getTo()} and then by their 325 | * {@link TemporalRange#getFrom()}. 326 | * 327 | * @return the {@link Comparator} 328 | */ 329 | public static Comparator> getToFromLocalDateTimeComparator() { 330 | return (range1, range2) -> { 331 | if (range1.getTo().equals(range2.getTo())) { 332 | return range1.getFrom().compareTo(range2.getFrom()); 333 | } else { 334 | return range1.getTo().compareTo(range2.getTo()); 335 | } 336 | }; 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = true 7 | max_line_length = 120 8 | tab_width = 4 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = true 13 | ij_smart_tabs = false 14 | ij_wrap_on_typing = false 15 | 16 | [*.java] 17 | ij_java_align_consecutive_assignments = false 18 | ij_java_align_consecutive_variable_declarations = false 19 | ij_java_align_group_field_declarations = false 20 | ij_java_align_multiline_annotation_parameters = false 21 | ij_java_align_multiline_array_initializer_expression = false 22 | ij_java_align_multiline_assignment = false 23 | ij_java_align_multiline_binary_operation = false 24 | ij_java_align_multiline_chained_methods = false 25 | ij_java_align_multiline_extends_list = false 26 | ij_java_align_multiline_for = false 27 | ij_java_align_multiline_method_parentheses = false 28 | ij_java_align_multiline_parameters = false 29 | ij_java_align_multiline_parameters_in_calls = false 30 | ij_java_align_multiline_parenthesized_expression = false 31 | ij_java_align_multiline_resources = false 32 | ij_java_align_multiline_ternary_operation = false 33 | ij_java_align_multiline_throws_list = false 34 | ij_java_align_subsequent_simple_methods = false 35 | ij_java_align_throws_keyword = false 36 | ij_java_annotation_parameter_wrap = normal 37 | ij_java_array_initializer_new_line_after_left_brace = false 38 | ij_java_array_initializer_right_brace_on_new_line = false 39 | ij_java_array_initializer_wrap = normal 40 | ij_java_assert_statement_colon_on_next_line = false 41 | ij_java_assert_statement_wrap = normal 42 | ij_java_assignment_wrap = normal 43 | ij_java_binary_operation_sign_on_next_line = false 44 | ij_java_binary_operation_wrap = normal 45 | ij_java_blank_lines_after_anonymous_class_header = 0 46 | ij_java_blank_lines_after_class_header = 1 47 | ij_java_blank_lines_after_imports = 1 48 | ij_java_blank_lines_after_package = 1 49 | ij_java_blank_lines_around_class = 1 50 | ij_java_blank_lines_around_field = 0 51 | ij_java_blank_lines_around_field_in_interface = 0 52 | ij_java_blank_lines_around_initializer = 1 53 | ij_java_blank_lines_around_method = 1 54 | ij_java_blank_lines_around_method_in_interface = 1 55 | ij_java_blank_lines_before_class_end = 0 56 | ij_java_blank_lines_before_imports = 1 57 | ij_java_blank_lines_before_method_body = 0 58 | ij_java_blank_lines_before_package = 0 59 | ij_java_block_brace_style = end_of_line 60 | ij_java_block_comment_at_first_column = true 61 | ij_java_call_parameters_new_line_after_left_paren = false 62 | ij_java_call_parameters_right_paren_on_new_line = false 63 | ij_java_call_parameters_wrap = normal 64 | ij_java_case_statement_on_separate_line = true 65 | ij_java_catch_on_new_line = false 66 | ij_java_class_annotation_wrap = split_into_lines 67 | ij_java_class_brace_style = end_of_line 68 | ij_java_class_count_to_use_import_on_demand = 20 69 | ij_java_class_names_in_javadoc = 1 70 | ij_java_do_not_indent_top_level_class_members = false 71 | ij_java_do_not_wrap_after_single_annotation = false 72 | ij_java_do_while_brace_force = always 73 | ij_java_doc_add_blank_line_after_description = true 74 | ij_java_doc_add_blank_line_after_param_comments = true 75 | ij_java_doc_add_blank_line_after_return = true 76 | ij_java_doc_add_p_tag_on_empty_lines = true 77 | ij_java_doc_align_exception_comments = true 78 | ij_java_doc_align_param_comments = true 79 | ij_java_doc_do_not_wrap_if_one_line = true 80 | ij_java_doc_enable_formatting = true 81 | ij_java_doc_enable_leading_asterisks = true 82 | ij_java_doc_indent_on_continuation = false 83 | ij_java_doc_keep_empty_lines = true 84 | ij_java_doc_keep_empty_parameter_tag = true 85 | ij_java_doc_keep_empty_return_tag = true 86 | ij_java_doc_keep_empty_throws_tag = true 87 | ij_java_doc_keep_invalid_tags = true 88 | ij_java_doc_param_description_on_new_line = false 89 | ij_java_doc_preserve_line_breaks = false 90 | ij_java_doc_use_throws_not_exception_tag = true 91 | ij_java_else_on_new_line = false 92 | ij_java_enum_constants_wrap = normal 93 | ij_java_extends_keyword_wrap = normal 94 | ij_java_extends_list_wrap = normal 95 | ij_java_field_annotation_wrap = split_into_lines 96 | ij_java_finally_on_new_line = false 97 | ij_java_for_brace_force = always 98 | ij_java_for_statement_new_line_after_left_paren = false 99 | ij_java_for_statement_right_paren_on_new_line = false 100 | ij_java_for_statement_wrap = normal 101 | ij_java_generate_final_locals = false 102 | ij_java_generate_final_parameters = false 103 | ij_java_if_brace_force = always 104 | ij_java_imports_layout = *,|,javax.**,java.**,|,$* 105 | ij_java_indent_case_from_switch = true 106 | ij_java_insert_inner_class_imports = false 107 | ij_java_insert_override_annotation = true 108 | ij_java_keep_blank_lines_before_right_brace = 1 109 | ij_java_keep_blank_lines_between_package_declaration_and_header = 1 110 | ij_java_keep_blank_lines_in_code = 1 111 | ij_java_keep_blank_lines_in_declarations = 1 112 | ij_java_keep_control_statement_in_one_line = true 113 | ij_java_keep_first_column_comment = true 114 | ij_java_keep_indents_on_empty_lines = false 115 | ij_java_keep_line_breaks = true 116 | ij_java_keep_multiple_expressions_in_one_line = true 117 | ij_java_keep_simple_blocks_in_one_line = true 118 | ij_java_keep_simple_classes_in_one_line = true 119 | ij_java_keep_simple_lambdas_in_one_line = true 120 | ij_java_keep_simple_methods_in_one_line = true 121 | ij_java_lambda_brace_style = end_of_line 122 | ij_java_layout_static_imports_separately = true 123 | ij_java_line_comment_add_space = true 124 | ij_java_line_comment_at_first_column = false 125 | ij_java_method_annotation_wrap = split_into_lines 126 | ij_java_method_brace_style = end_of_line 127 | ij_java_method_call_chain_wrap = normal 128 | ij_java_method_parameters_new_line_after_left_paren = false 129 | ij_java_method_parameters_right_paren_on_new_line = false 130 | ij_java_method_parameters_wrap = normal 131 | ij_java_modifier_list_wrap = false 132 | ij_java_names_count_to_use_import_on_demand = 20 133 | ij_java_parameter_annotation_wrap = normal 134 | ij_java_parentheses_expression_new_line_after_left_paren = false 135 | ij_java_parentheses_expression_right_paren_on_new_line = false 136 | ij_java_place_assignment_sign_on_next_line = false 137 | ij_java_prefer_longer_names = true 138 | ij_java_prefer_parameters_wrap = false 139 | ij_java_repeat_synchronized = true 140 | ij_java_replace_instanceof_and_cast = false 141 | ij_java_replace_null_check = true 142 | ij_java_replace_sum_lambda_with_method_ref = true 143 | ij_java_resource_list_new_line_after_left_paren = false 144 | ij_java_resource_list_right_paren_on_new_line = false 145 | ij_java_resource_list_wrap = normal 146 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 147 | ij_java_space_after_colon = true 148 | ij_java_space_after_comma = true 149 | ij_java_space_after_comma_in_type_arguments = true 150 | ij_java_space_after_for_semicolon = true 151 | ij_java_space_after_quest = true 152 | ij_java_space_after_type_cast = true 153 | ij_java_space_before_annotation_array_initializer_left_brace = false 154 | ij_java_space_before_annotation_parameter_list = false 155 | ij_java_space_before_array_initializer_left_brace = false 156 | ij_java_space_before_catch_keyword = true 157 | ij_java_space_before_catch_left_brace = true 158 | ij_java_space_before_catch_parentheses = true 159 | ij_java_space_before_class_left_brace = true 160 | ij_java_space_before_colon = true 161 | ij_java_space_before_colon_in_foreach = true 162 | ij_java_space_before_comma = false 163 | ij_java_space_before_do_left_brace = true 164 | ij_java_space_before_else_keyword = true 165 | ij_java_space_before_else_left_brace = true 166 | ij_java_space_before_finally_keyword = true 167 | ij_java_space_before_finally_left_brace = true 168 | ij_java_space_before_for_left_brace = true 169 | ij_java_space_before_for_parentheses = true 170 | ij_java_space_before_for_semicolon = false 171 | ij_java_space_before_if_left_brace = true 172 | ij_java_space_before_if_parentheses = true 173 | ij_java_space_before_method_call_parentheses = false 174 | ij_java_space_before_method_left_brace = true 175 | ij_java_space_before_method_parentheses = false 176 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 177 | ij_java_space_before_quest = true 178 | ij_java_space_before_switch_left_brace = true 179 | ij_java_space_before_switch_parentheses = true 180 | ij_java_space_before_synchronized_left_brace = true 181 | ij_java_space_before_synchronized_parentheses = true 182 | ij_java_space_before_try_left_brace = true 183 | ij_java_space_before_try_parentheses = true 184 | ij_java_space_before_type_parameter_list = false 185 | ij_java_space_before_while_keyword = true 186 | ij_java_space_before_while_left_brace = true 187 | ij_java_space_before_while_parentheses = true 188 | ij_java_space_inside_one_line_enum_braces = false 189 | ij_java_space_within_empty_array_initializer_braces = false 190 | ij_java_space_within_empty_method_call_parentheses = false 191 | ij_java_space_within_empty_method_parentheses = false 192 | ij_java_spaces_around_additive_operators = true 193 | ij_java_spaces_around_assignment_operators = true 194 | ij_java_spaces_around_bitwise_operators = true 195 | ij_java_spaces_around_equality_operators = true 196 | ij_java_spaces_around_lambda_arrow = true 197 | ij_java_spaces_around_logical_operators = true 198 | ij_java_spaces_around_method_ref_dbl_colon = false 199 | ij_java_spaces_around_multiplicative_operators = true 200 | ij_java_spaces_around_relational_operators = true 201 | ij_java_spaces_around_shift_operators = true 202 | ij_java_spaces_around_type_bounds_in_type_parameters = true 203 | ij_java_spaces_around_unary_operator = false 204 | ij_java_spaces_within_angle_brackets = false 205 | ij_java_spaces_within_annotation_parentheses = false 206 | ij_java_spaces_within_array_initializer_braces = false 207 | ij_java_spaces_within_braces = false 208 | ij_java_spaces_within_brackets = false 209 | ij_java_spaces_within_cast_parentheses = false 210 | ij_java_spaces_within_catch_parentheses = false 211 | ij_java_spaces_within_for_parentheses = false 212 | ij_java_spaces_within_if_parentheses = false 213 | ij_java_spaces_within_method_call_parentheses = false 214 | ij_java_spaces_within_method_parentheses = false 215 | ij_java_spaces_within_parentheses = false 216 | ij_java_spaces_within_switch_parentheses = false 217 | ij_java_spaces_within_synchronized_parentheses = false 218 | ij_java_spaces_within_try_parentheses = false 219 | ij_java_spaces_within_while_parentheses = false 220 | ij_java_special_else_if_treatment = true 221 | ij_java_subclass_name_suffix = Impl 222 | ij_java_ternary_operation_signs_on_next_line = false 223 | ij_java_ternary_operation_wrap = normal 224 | ij_java_test_name_suffix = Test 225 | ij_java_throws_keyword_wrap = normal 226 | ij_java_throws_list_wrap = normal 227 | ij_java_use_external_annotations = false 228 | ij_java_use_fq_class_names = false 229 | ij_java_use_single_class_imports = true 230 | ij_java_variable_annotation_wrap = normal 231 | ij_java_visibility = public 232 | ij_java_while_brace_force = always 233 | ij_java_while_on_new_line = false 234 | ij_java_wrap_comments = true 235 | ij_java_wrap_first_method_in_call_chain = false 236 | ij_java_wrap_long_lines = true 237 | 238 | [*.json] 239 | indent_size = 2 240 | ij_json_keep_blank_lines_in_code = 0 241 | ij_json_keep_indents_on_empty_lines = false 242 | ij_json_keep_line_breaks = true 243 | ij_json_space_after_colon = true 244 | ij_json_space_after_comma = true 245 | ij_json_space_before_colon = true 246 | ij_json_space_before_comma = false 247 | ij_json_spaces_within_braces = false 248 | ij_json_spaces_within_brackets = false 249 | ij_json_wrap_long_lines = false 250 | 251 | [*.properties] 252 | ij_properties_align_group_field_declarations = false 253 | 254 | [*.scala] 255 | indent_size = 2 256 | tab_width = 2 257 | ij_continuation_indent_size = 2 258 | ij_scala_align_composite_pattern = true 259 | ij_scala_align_extends_with = 0 260 | ij_scala_align_group_field_declarations = false 261 | ij_scala_align_if_else = false 262 | ij_scala_align_in_columns_case_branch = false 263 | ij_scala_align_multiline_binary_operation = false 264 | ij_scala_align_multiline_chained_methods = false 265 | ij_scala_align_multiline_for = true 266 | ij_scala_align_multiline_parameters = true 267 | ij_scala_align_multiline_parameters_in_calls = false 268 | ij_scala_align_multiline_parenthesized_expression = false 269 | ij_scala_align_tuple_elements = false 270 | ij_scala_align_types_in_multiline_declarations = false 271 | ij_scala_all_other_imports = all other imports 272 | ij_scala_alternate_continuation_indent_for_params = 4 273 | ij_scala_binary_operation_wrap = off 274 | ij_scala_blank_line = _______ blank line _______ 275 | ij_scala_blank_lines_after_anonymous_class_header = 0 276 | ij_scala_blank_lines_after_class_header = 0 277 | ij_scala_blank_lines_after_imports = 1 278 | ij_scala_blank_lines_after_package = 1 279 | ij_scala_blank_lines_around_class = 1 280 | ij_scala_blank_lines_around_field = 0 281 | ij_scala_blank_lines_around_field_in_inner_scopes = 0 282 | ij_scala_blank_lines_around_field_in_interface = 0 283 | ij_scala_blank_lines_around_method = 1 284 | ij_scala_blank_lines_around_method_in_inner_scopes = 1 285 | ij_scala_blank_lines_around_method_in_interface = 1 286 | ij_scala_blank_lines_before_imports = 1 287 | ij_scala_blank_lines_before_method_body = 0 288 | ij_scala_blank_lines_before_package = 0 289 | ij_scala_block_brace_style = end_of_line 290 | ij_scala_block_comment_at_first_column = true 291 | ij_scala_call_parameters_new_line_after_lparen = 0 292 | ij_scala_call_parameters_right_paren_on_new_line = false 293 | ij_scala_call_parameters_wrap = off 294 | ij_scala_case_clause_brace_force = never 295 | ij_scala_catch_on_new_line = false 296 | ij_scala_class_annotation_wrap = split_into_lines 297 | ij_scala_class_brace_style = end_of_line 298 | ij_scala_closure_brace_force = never 299 | ij_scala_do_not_align_block_expr_params = true 300 | ij_scala_do_not_indent_case_clause_body = false 301 | ij_scala_do_not_indent_tuples_close_brace = true 302 | ij_scala_do_while_brace_force = never 303 | ij_scala_else_on_new_line = false 304 | ij_scala_enable_scaladoc_formatting = true 305 | ij_scala_enforce_functional_syntax_for_unit = true 306 | ij_scala_exclude_prefix = exclude: 307 | ij_scala_extends_keyword_wrap = off 308 | ij_scala_extends_list_wrap = off 309 | ij_scala_field_annotation_wrap = split_into_lines 310 | ij_scala_finally_brace_force = never 311 | ij_scala_finally_on_new_line = false 312 | ij_scala_for_brace_force = never 313 | ij_scala_for_statement_wrap = off 314 | ij_scala_formatter = 0 315 | ij_scala_if_brace_force = never 316 | ij_scala_indent_braced_function_args = true 317 | ij_scala_indent_case_from_switch = true 318 | ij_scala_indent_first_parameter = true 319 | ij_scala_indent_first_parameter_clause = false 320 | ij_scala_indent_type_arguments = true 321 | ij_scala_indent_type_parameters = true 322 | ij_scala_insert_whitespaces_in_simple_one_line_method = true 323 | ij_scala_keep_blank_lines_before_right_brace = 2 324 | ij_scala_keep_blank_lines_in_code = 2 325 | ij_scala_keep_blank_lines_in_declarations = 2 326 | ij_scala_keep_comments_on_same_line = true 327 | ij_scala_keep_first_column_comment = false 328 | ij_scala_keep_indents_on_empty_lines = false 329 | ij_scala_keep_line_breaks = true 330 | ij_scala_keep_one_line_lambdas_in_arg_list = false 331 | ij_scala_keep_simple_blocks_in_one_line = false 332 | ij_scala_keep_simple_methods_in_one_line = false 333 | ij_scala_keep_xml_formatting = false 334 | ij_scala_line_comment_at_first_column = true 335 | ij_scala_method_annotation_wrap = split_into_lines 336 | ij_scala_method_brace_force = never 337 | ij_scala_method_brace_style = end_of_line 338 | ij_scala_method_call_chain_wrap = off 339 | ij_scala_method_parameters_new_line_after_left_paren = false 340 | ij_scala_method_parameters_right_paren_on_new_line = false 341 | ij_scala_method_parameters_wrap = off 342 | ij_scala_modifier_list_wrap = false 343 | ij_scala_multiline_string_align_dangling_closing_quotes = false 344 | ij_scala_multiline_string_closing_quotes_on_new_line = true 345 | ij_scala_multiline_string_insert_margin_on_enter = true 346 | ij_scala_multiline_string_margin_char = | 347 | ij_scala_multiline_string_margin_indent = 2 348 | ij_scala_multiline_string_opening_quotes_on_new_line = true 349 | ij_scala_multiline_string_process_margin_on_copy_paste = true 350 | ij_scala_newline_after_annotations = false 351 | ij_scala_not_continuation_indent_for_params = false 352 | ij_scala_parameter_annotation_wrap = off 353 | ij_scala_parentheses_expression_new_line_after_left_paren = false 354 | ij_scala_parentheses_expression_right_paren_on_new_line = false 355 | ij_scala_place_closure_parameters_on_new_line = false 356 | ij_scala_place_self_type_on_new_line = true 357 | ij_scala_prefer_parameters_wrap = false 358 | ij_scala_preserve_space_after_method_declaration_name = false 359 | ij_scala_reformat_on_compile = false 360 | ij_scala_replace_case_arrow_with_unicode_char = false 361 | ij_scala_replace_for_generator_arrow_with_unicode_char = false 362 | ij_scala_replace_lambda_with_greek_letter = false 363 | ij_scala_replace_map_arrow_with_unicode_char = false 364 | ij_scala_scalafmt_reformat_on_files_save = false 365 | ij_scala_scalafmt_show_invalid_code_warnings = true 366 | ij_scala_scalafmt_use_intellij_formatter_for_range_format = true 367 | ij_scala_sd_align_exception_comments = true 368 | ij_scala_sd_align_other_tags_comments = true 369 | ij_scala_sd_align_parameters_comments = true 370 | ij_scala_sd_align_return_comments = true 371 | ij_scala_sd_blank_line_after_parameters_comments = false 372 | ij_scala_sd_blank_line_after_return_comments = false 373 | ij_scala_sd_blank_line_before_parameters = false 374 | ij_scala_sd_blank_line_before_tags = true 375 | ij_scala_sd_blank_line_between_parameters = false 376 | ij_scala_sd_keep_blank_lines_between_tags = false 377 | ij_scala_sd_preserve_spaces_in_tags = false 378 | ij_scala_space_after_comma = true 379 | ij_scala_space_after_for_semicolon = true 380 | ij_scala_space_after_modifiers_constructor = false 381 | ij_scala_space_after_type_colon = true 382 | ij_scala_space_before_brace_method_call = true 383 | ij_scala_space_before_class_left_brace = true 384 | ij_scala_space_before_infix_like_method_parentheses = false 385 | ij_scala_space_before_infix_method_call_parentheses = false 386 | ij_scala_space_before_infix_operator_like_method_call_parentheses = true 387 | ij_scala_space_before_method_call_parentheses = false 388 | ij_scala_space_before_method_left_brace = true 389 | ij_scala_space_before_method_parentheses = false 390 | ij_scala_space_before_type_colon = false 391 | ij_scala_space_before_type_parameter_in_def_list = false 392 | ij_scala_space_before_type_parameter_list = false 393 | ij_scala_space_inside_closure_braces = true 394 | ij_scala_space_inside_self_type_braces = true 395 | ij_scala_space_within_empty_method_call_parentheses = false 396 | ij_scala_spaces_around_at_in_patterns = false 397 | ij_scala_spaces_in_imports = false 398 | ij_scala_spaces_in_one_line_blocks = false 399 | ij_scala_spaces_within_brackets = false 400 | ij_scala_spaces_within_for_parentheses = false 401 | ij_scala_spaces_within_if_parentheses = false 402 | ij_scala_spaces_within_method_call_parentheses = false 403 | ij_scala_spaces_within_method_parentheses = false 404 | ij_scala_spaces_within_parentheses = false 405 | ij_scala_spaces_within_while_parentheses = false 406 | ij_scala_special_else_if_treatment = true 407 | ij_scala_trailing_comma_arg_list_enabled = true 408 | ij_scala_trailing_comma_import_selector_enabled = false 409 | ij_scala_trailing_comma_mode = trailing_comma_keep 410 | ij_scala_trailing_comma_params_enabled = true 411 | ij_scala_trailing_comma_pattern_arg_list_enabled = false 412 | ij_scala_trailing_comma_tuple_enabled = false 413 | ij_scala_trailing_comma_tuple_type_enabled = false 414 | ij_scala_trailing_comma_type_params_enabled = false 415 | ij_scala_try_brace_force = never 416 | ij_scala_type_annotation_exclude_constant = true 417 | ij_scala_type_annotation_exclude_in_dialect_sources = true 418 | ij_scala_type_annotation_exclude_in_test_sources = false 419 | ij_scala_type_annotation_exclude_member_of_anonymous_class = false 420 | ij_scala_type_annotation_exclude_member_of_private_class = false 421 | ij_scala_type_annotation_exclude_when_type_is_stable = true 422 | ij_scala_type_annotation_function_parameter = false 423 | ij_scala_type_annotation_implicit_modifier = true 424 | ij_scala_type_annotation_local_definition = false 425 | ij_scala_type_annotation_private_member = false 426 | ij_scala_type_annotation_protected_member = true 427 | ij_scala_type_annotation_public_member = true 428 | ij_scala_type_annotation_structural_type = true 429 | ij_scala_type_annotation_underscore_parameter = false 430 | ij_scala_type_annotation_unit_type = true 431 | ij_scala_use_alternate_continuation_indent_for_params = false 432 | ij_scala_use_scaladoc2_formatting = false 433 | ij_scala_variable_annotation_wrap = off 434 | ij_scala_while_brace_force = never 435 | ij_scala_while_on_new_line = false 436 | ij_scala_wrap_before_with_keyword = false 437 | ij_scala_wrap_first_method_in_call_chain = false 438 | ij_scala_wrap_long_lines = false 439 | 440 | [.editorconfig] 441 | ij_editorconfig_align_group_field_declarations = false 442 | ij_editorconfig_space_after_colon = false 443 | ij_editorconfig_space_after_comma = true 444 | ij_editorconfig_space_before_colon = false 445 | ij_editorconfig_space_before_comma = false 446 | ij_editorconfig_spaces_around_assignment_operators = true 447 | 448 | [{*.gant,*.groovy,*.gradle,*.gdsl,*.gy}] 449 | ij_groovy_align_group_field_declarations = false 450 | ij_groovy_align_multiline_array_initializer_expression = false 451 | ij_groovy_align_multiline_assignment = false 452 | ij_groovy_align_multiline_binary_operation = false 453 | ij_groovy_align_multiline_chained_methods = false 454 | ij_groovy_align_multiline_extends_list = false 455 | ij_groovy_align_multiline_for = true 456 | ij_groovy_align_multiline_method_parentheses = false 457 | ij_groovy_align_multiline_parameters = true 458 | ij_groovy_align_multiline_parameters_in_calls = false 459 | ij_groovy_align_multiline_resources = true 460 | ij_groovy_align_multiline_ternary_operation = false 461 | ij_groovy_align_multiline_throws_list = false 462 | ij_groovy_align_throws_keyword = false 463 | ij_groovy_array_initializer_new_line_after_left_brace = false 464 | ij_groovy_array_initializer_right_brace_on_new_line = false 465 | ij_groovy_array_initializer_wrap = off 466 | ij_groovy_assert_statement_wrap = off 467 | ij_groovy_assignment_wrap = off 468 | ij_groovy_binary_operation_wrap = off 469 | ij_groovy_blank_lines_after_class_header = 0 470 | ij_groovy_blank_lines_after_imports = 1 471 | ij_groovy_blank_lines_after_package = 1 472 | ij_groovy_blank_lines_around_class = 1 473 | ij_groovy_blank_lines_around_field = 0 474 | ij_groovy_blank_lines_around_field_in_interface = 0 475 | ij_groovy_blank_lines_around_method = 1 476 | ij_groovy_blank_lines_around_method_in_interface = 1 477 | ij_groovy_blank_lines_before_imports = 1 478 | ij_groovy_blank_lines_before_method_body = 0 479 | ij_groovy_blank_lines_before_package = 0 480 | ij_groovy_block_brace_style = end_of_line 481 | ij_groovy_block_comment_at_first_column = true 482 | ij_groovy_call_parameters_new_line_after_left_paren = false 483 | ij_groovy_call_parameters_right_paren_on_new_line = false 484 | ij_groovy_call_parameters_wrap = off 485 | ij_groovy_catch_on_new_line = false 486 | ij_groovy_class_annotation_wrap = split_into_lines 487 | ij_groovy_class_brace_style = end_of_line 488 | ij_groovy_do_while_brace_force = never 489 | ij_groovy_else_on_new_line = false 490 | ij_groovy_enum_constants_wrap = off 491 | ij_groovy_extends_keyword_wrap = off 492 | ij_groovy_extends_list_wrap = off 493 | ij_groovy_field_annotation_wrap = split_into_lines 494 | ij_groovy_finally_on_new_line = false 495 | ij_groovy_for_brace_force = never 496 | ij_groovy_for_statement_new_line_after_left_paren = false 497 | ij_groovy_for_statement_right_paren_on_new_line = false 498 | ij_groovy_for_statement_wrap = off 499 | ij_groovy_if_brace_force = never 500 | ij_groovy_indent_case_from_switch = true 501 | ij_groovy_keep_blank_lines_before_right_brace = 2 502 | ij_groovy_keep_blank_lines_in_code = 2 503 | ij_groovy_keep_blank_lines_in_declarations = 2 504 | ij_groovy_keep_control_statement_in_one_line = true 505 | ij_groovy_keep_first_column_comment = true 506 | ij_groovy_keep_indents_on_empty_lines = false 507 | ij_groovy_keep_line_breaks = true 508 | ij_groovy_keep_multiple_expressions_in_one_line = false 509 | ij_groovy_keep_simple_blocks_in_one_line = false 510 | ij_groovy_keep_simple_classes_in_one_line = true 511 | ij_groovy_keep_simple_lambdas_in_one_line = true 512 | ij_groovy_keep_simple_methods_in_one_line = true 513 | ij_groovy_lambda_brace_style = end_of_line 514 | ij_groovy_line_comment_add_space = false 515 | ij_groovy_line_comment_at_first_column = true 516 | ij_groovy_method_annotation_wrap = split_into_lines 517 | ij_groovy_method_brace_style = end_of_line 518 | ij_groovy_method_call_chain_wrap = off 519 | ij_groovy_method_parameters_new_line_after_left_paren = false 520 | ij_groovy_method_parameters_right_paren_on_new_line = false 521 | ij_groovy_method_parameters_wrap = off 522 | ij_groovy_modifier_list_wrap = false 523 | ij_groovy_parameter_annotation_wrap = off 524 | ij_groovy_parentheses_expression_new_line_after_left_paren = false 525 | ij_groovy_parentheses_expression_right_paren_on_new_line = false 526 | ij_groovy_prefer_parameters_wrap = false 527 | ij_groovy_resource_list_new_line_after_left_paren = false 528 | ij_groovy_resource_list_right_paren_on_new_line = false 529 | ij_groovy_resource_list_wrap = off 530 | ij_groovy_space_after_colon = true 531 | ij_groovy_space_after_comma = true 532 | ij_groovy_space_after_comma_in_type_arguments = true 533 | ij_groovy_space_after_for_semicolon = true 534 | ij_groovy_space_after_quest = true 535 | ij_groovy_space_after_type_cast = true 536 | ij_groovy_space_before_annotation_parameter_list = false 537 | ij_groovy_space_before_array_initializer_left_brace = false 538 | ij_groovy_space_before_catch_keyword = true 539 | ij_groovy_space_before_catch_left_brace = true 540 | ij_groovy_space_before_catch_parentheses = true 541 | ij_groovy_space_before_class_left_brace = true 542 | ij_groovy_space_before_colon = true 543 | ij_groovy_space_before_comma = false 544 | ij_groovy_space_before_do_left_brace = true 545 | ij_groovy_space_before_else_keyword = true 546 | ij_groovy_space_before_else_left_brace = true 547 | ij_groovy_space_before_finally_keyword = true 548 | ij_groovy_space_before_finally_left_brace = true 549 | ij_groovy_space_before_for_left_brace = true 550 | ij_groovy_space_before_for_parentheses = true 551 | ij_groovy_space_before_for_semicolon = false 552 | ij_groovy_space_before_if_left_brace = true 553 | ij_groovy_space_before_if_parentheses = true 554 | ij_groovy_space_before_method_call_parentheses = false 555 | ij_groovy_space_before_method_left_brace = true 556 | ij_groovy_space_before_method_parentheses = false 557 | ij_groovy_space_before_quest = true 558 | ij_groovy_space_before_switch_left_brace = true 559 | ij_groovy_space_before_switch_parentheses = true 560 | ij_groovy_space_before_synchronized_left_brace = true 561 | ij_groovy_space_before_synchronized_parentheses = true 562 | ij_groovy_space_before_try_left_brace = true 563 | ij_groovy_space_before_try_parentheses = true 564 | ij_groovy_space_before_while_keyword = true 565 | ij_groovy_space_before_while_left_brace = true 566 | ij_groovy_space_before_while_parentheses = true 567 | ij_groovy_space_within_empty_array_initializer_braces = false 568 | ij_groovy_space_within_empty_method_call_parentheses = false 569 | ij_groovy_spaces_around_additive_operators = true 570 | ij_groovy_spaces_around_assignment_operators = true 571 | ij_groovy_spaces_around_bitwise_operators = true 572 | ij_groovy_spaces_around_equality_operators = true 573 | ij_groovy_spaces_around_lambda_arrow = true 574 | ij_groovy_spaces_around_logical_operators = true 575 | ij_groovy_spaces_around_multiplicative_operators = true 576 | ij_groovy_spaces_around_relational_operators = true 577 | ij_groovy_spaces_around_shift_operators = true 578 | ij_groovy_spaces_within_annotation_parentheses = false 579 | ij_groovy_spaces_within_array_initializer_braces = false 580 | ij_groovy_spaces_within_braces = true 581 | ij_groovy_spaces_within_brackets = false 582 | ij_groovy_spaces_within_cast_parentheses = false 583 | ij_groovy_spaces_within_catch_parentheses = false 584 | ij_groovy_spaces_within_for_parentheses = false 585 | ij_groovy_spaces_within_if_parentheses = false 586 | ij_groovy_spaces_within_method_call_parentheses = false 587 | ij_groovy_spaces_within_method_parentheses = false 588 | ij_groovy_spaces_within_parentheses = false 589 | ij_groovy_spaces_within_switch_parentheses = false 590 | ij_groovy_spaces_within_synchronized_parentheses = false 591 | ij_groovy_spaces_within_try_parentheses = false 592 | ij_groovy_spaces_within_while_parentheses = false 593 | ij_groovy_special_else_if_treatment = true 594 | ij_groovy_ternary_operation_wrap = off 595 | ij_groovy_throws_keyword_wrap = off 596 | ij_groovy_throws_list_wrap = off 597 | ij_groovy_variable_annotation_wrap = off 598 | ij_groovy_while_brace_force = never 599 | ij_groovy_while_on_new_line = false 600 | ij_groovy_wrap_long_lines = false 601 | 602 | [{*.jhm,*.xslt,*.xul,*.tagx,*.rng,*.xsl,*.xsd,*.jspx,*.ant,*.xml,*.tld,*.fxml,*.wsdl,*.jrxml,*.jnlp,*.pom}] 603 | ij_xml_block_comment_at_first_column = true 604 | ij_xml_keep_indents_on_empty_lines = false 605 | ij_xml_line_comment_at_first_column = true 606 | 607 | [{*.kt,*.kts}] 608 | ij_kotlin_align_in_columns_case_branch = false 609 | ij_kotlin_align_multiline_binary_operation = false 610 | ij_kotlin_align_multiline_extends_list = false 611 | ij_kotlin_align_multiline_method_parentheses = false 612 | ij_kotlin_align_multiline_parameters = true 613 | ij_kotlin_align_multiline_parameters_in_calls = false 614 | ij_kotlin_assignment_wrap = off 615 | ij_kotlin_blank_lines_after_class_header = 0 616 | ij_kotlin_blank_lines_around_block_when_branches = 0 617 | ij_kotlin_block_comment_at_first_column = true 618 | ij_kotlin_call_parameters_new_line_after_left_paren = false 619 | ij_kotlin_call_parameters_right_paren_on_new_line = false 620 | ij_kotlin_call_parameters_wrap = off 621 | ij_kotlin_catch_on_new_line = false 622 | ij_kotlin_class_annotation_wrap = split_into_lines 623 | ij_kotlin_continuation_indent_for_chained_calls = true 624 | ij_kotlin_continuation_indent_for_expression_bodies = true 625 | ij_kotlin_continuation_indent_in_argument_lists = true 626 | ij_kotlin_continuation_indent_in_elvis = true 627 | ij_kotlin_continuation_indent_in_if_conditions = true 628 | ij_kotlin_continuation_indent_in_parameter_lists = true 629 | ij_kotlin_continuation_indent_in_supertype_lists = true 630 | ij_kotlin_else_on_new_line = false 631 | ij_kotlin_enum_constants_wrap = off 632 | ij_kotlin_extends_list_wrap = off 633 | ij_kotlin_field_annotation_wrap = split_into_lines 634 | ij_kotlin_finally_on_new_line = false 635 | ij_kotlin_if_rparen_on_new_line = false 636 | ij_kotlin_import_nested_classes = false 637 | ij_kotlin_insert_whitespaces_in_simple_one_line_method = true 638 | ij_kotlin_keep_blank_lines_before_right_brace = 2 639 | ij_kotlin_keep_blank_lines_in_code = 2 640 | ij_kotlin_keep_blank_lines_in_declarations = 2 641 | ij_kotlin_keep_first_column_comment = true 642 | ij_kotlin_keep_indents_on_empty_lines = false 643 | ij_kotlin_keep_line_breaks = true 644 | ij_kotlin_lbrace_on_next_line = false 645 | ij_kotlin_line_comment_add_space = false 646 | ij_kotlin_line_comment_at_first_column = true 647 | ij_kotlin_method_annotation_wrap = split_into_lines 648 | ij_kotlin_method_call_chain_wrap = off 649 | ij_kotlin_method_parameters_new_line_after_left_paren = false 650 | ij_kotlin_method_parameters_right_paren_on_new_line = false 651 | ij_kotlin_method_parameters_wrap = off 652 | ij_kotlin_name_count_to_use_star_import = 5 653 | ij_kotlin_name_count_to_use_star_import_for_members = 3 654 | ij_kotlin_parameter_annotation_wrap = off 655 | ij_kotlin_space_after_comma = true 656 | ij_kotlin_space_after_extend_colon = true 657 | ij_kotlin_space_after_type_colon = true 658 | ij_kotlin_space_before_catch_parentheses = true 659 | ij_kotlin_space_before_comma = false 660 | ij_kotlin_space_before_extend_colon = true 661 | ij_kotlin_space_before_for_parentheses = true 662 | ij_kotlin_space_before_if_parentheses = true 663 | ij_kotlin_space_before_lambda_arrow = true 664 | ij_kotlin_space_before_type_colon = false 665 | ij_kotlin_space_before_when_parentheses = true 666 | ij_kotlin_space_before_while_parentheses = true 667 | ij_kotlin_spaces_around_additive_operators = true 668 | ij_kotlin_spaces_around_assignment_operators = true 669 | ij_kotlin_spaces_around_equality_operators = true 670 | ij_kotlin_spaces_around_function_type_arrow = true 671 | ij_kotlin_spaces_around_logical_operators = true 672 | ij_kotlin_spaces_around_multiplicative_operators = true 673 | ij_kotlin_spaces_around_range = false 674 | ij_kotlin_spaces_around_relational_operators = true 675 | ij_kotlin_spaces_around_unary_operator = false 676 | ij_kotlin_spaces_around_when_arrow = true 677 | ij_kotlin_variable_annotation_wrap = off 678 | ij_kotlin_while_on_new_line = false 679 | ij_kotlin_wrap_elvis_expressions = 1 680 | ij_kotlin_wrap_expression_body_functions = 0 681 | ij_kotlin_wrap_first_method_in_call_chain = false 682 | 683 | [{*.shtm,*.htm,*.sht,*.shtml,*.html}] 684 | ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 685 | ij_html_align_attributes = true 686 | ij_html_align_text = false 687 | ij_html_attribute_wrap = normal 688 | ij_html_block_comment_at_first_column = true 689 | ij_html_do_not_align_children_of_min_lines = 0 690 | ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p 691 | ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot 692 | ij_html_enforce_quotes = false 693 | ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var 694 | ij_html_keep_blank_lines = 2 695 | ij_html_keep_indents_on_empty_lines = false 696 | ij_html_keep_line_breaks = true 697 | ij_html_keep_line_breaks_in_text = true 698 | ij_html_keep_whitespaces = false 699 | ij_html_keep_whitespaces_inside = span,pre,textarea 700 | ij_html_line_comment_at_first_column = true 701 | ij_html_new_line_after_last_attribute = never 702 | ij_html_new_line_before_first_attribute = never 703 | ij_html_quote_style = double 704 | ij_html_remove_new_line_before_tags = br 705 | ij_html_space_after_tag_name = false 706 | ij_html_space_around_equality_in_attribute = false 707 | ij_html_space_inside_empty_tag = false 708 | ij_html_text_wrap = normal 709 | 710 | [{*.yml,*.yaml}] 711 | indent_size = 2 712 | ij_continuation_indent_size = 2 713 | ij_yaml_keep_indents_on_empty_lines = false 714 | ij_yaml_keep_line_breaks = true 715 | 716 | [{*.zsh,*.bash,*.sh}] 717 | ij_shell_binary_ops_start_line = false 718 | ij_shell_keep_column_alignment_padding = false 719 | ij_shell_minify_program = false 720 | ij_shell_redirect_followed_by_space = false 721 | ij_shell_switch_cases_indented = false 722 | --------------------------------------------------------------------------------